diff --git a/tools/go.mod b/tools/go.mod index c703015d23..8a43bd431d 100644 --- a/tools/go.mod +++ b/tools/go.mod @@ -1,10 +1,11 @@ module github.com/crc-org/crc/tools go 1.22.0 +toolchain go1.22.5 require ( github.com/cfergeau/gomod2rpmdeps v0.0.0-20210223144124-2042c4850ca8 - github.com/golangci/golangci-lint v1.59.1 + github.com/golangci/golangci-lint v1.61.0 github.com/randall77/makefat v0.0.0-20210315173500-7ddd0e42c844 golang.org/x/tools v0.24.0 ) @@ -13,15 +14,15 @@ require ( 4d63.com/gocheckcompilerdirectives v1.2.1 // indirect 4d63.com/gochecknoglobals v0.2.1 // indirect github.com/4meepo/tagalign v1.3.4 // indirect - github.com/Abirdcfly/dupword v0.0.14 // indirect + github.com/Abirdcfly/dupword v0.1.1 // indirect github.com/Antonboom/errname v0.1.13 // indirect github.com/Antonboom/nilnil v0.1.9 // indirect - github.com/Antonboom/testifylint v1.3.1 // indirect - github.com/BurntSushi/toml v1.4.0 // indirect - github.com/Crocmagnon/fatcontext v0.2.2 // indirect + github.com/Antonboom/testifylint v1.4.3 // indirect + github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect + github.com/Crocmagnon/fatcontext v0.5.2 // indirect github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 // indirect - github.com/GaijinEntertainment/go-exhaustruct/v3 v3.2.0 // indirect - github.com/Masterminds/semver/v3 v3.2.1 // indirect + github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0 // indirect + github.com/Masterminds/semver/v3 v3.3.0 // indirect github.com/OpenPeeDeeP/depguard/v2 v2.2.0 // indirect github.com/alecthomas/go-check-sumtype v0.1.4 // indirect github.com/alexkohler/nakedret/v2 v2.0.4 // indirect @@ -32,7 +33,7 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/bkielbasa/cyclop v1.2.1 // indirect github.com/blizzy78/varnamelen v0.8.0 // indirect - github.com/bombsimon/wsl/v4 v4.2.1 // indirect + github.com/bombsimon/wsl/v4 v4.4.1 // indirect github.com/breml/bidichk v0.2.7 // indirect github.com/breml/errchkjson v0.3.6 // indirect github.com/butuzov/ireturn v0.3.0 // indirect @@ -42,9 +43,9 @@ require ( github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/charithe/durationcheck v0.0.10 // indirect github.com/chavacava/garif v0.1.0 // indirect - github.com/ckaznocha/intrange v0.1.2 // indirect + github.com/ckaznocha/intrange v0.2.0 // indirect github.com/curioswitch/go-reassign v0.2.0 // indirect - github.com/daixiang0/gci v0.13.4 // indirect + github.com/daixiang0/gci v0.13.5 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/denis-tingaikin/go-header v0.5.0 // indirect github.com/ettle/strcase v0.2.0 // indirect @@ -62,13 +63,13 @@ require ( github.com/go-toolsmith/astp v1.1.0 // indirect github.com/go-toolsmith/strparse v1.1.0 // indirect github.com/go-toolsmith/typep v1.1.0 // indirect - github.com/go-viper/mapstructure/v2 v2.0.0 // indirect + github.com/go-viper/mapstructure/v2 v2.1.0 // indirect github.com/go-xmlfmt/xmlfmt v1.1.2 // indirect github.com/gobwas/glob v0.2.3 // indirect - github.com/gofrs/flock v0.8.1 // indirect + github.com/gofrs/flock v0.12.1 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a // indirect - github.com/golangci/gofmt v0.0.0-20231018234816-f50ced29576e // indirect + github.com/golangci/gofmt v0.0.0-20240816233607-d8596aa466a9 // indirect github.com/golangci/misspell v0.6.0 // indirect github.com/golangci/modinfo v0.3.4 // indirect github.com/golangci/plugin-module-register v0.1.1 // indirect @@ -87,7 +88,7 @@ require ( github.com/jgautheron/goconst v1.7.1 // indirect github.com/jingyugao/rowserrcheck v1.1.1 // indirect github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af // indirect - github.com/jjti/go-spancheck v0.6.1 // indirect + github.com/jjti/go-spancheck v0.6.2 // indirect github.com/julz/importas v0.1.0 // indirect github.com/karamaru-alpha/copyloopvar v1.1.0 // indirect github.com/kisielk/errcheck v1.7.0 // indirect @@ -109,45 +110,44 @@ require ( github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.9 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect - github.com/mgechev/revive v1.3.7 // indirect + github.com/mgechev/revive v1.3.9 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/moricho/tparallel v0.3.1 // indirect + github.com/moricho/tparallel v0.3.2 // indirect github.com/nakabonne/nestif v0.3.1 // indirect - github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/nishanths/exhaustive v0.12.0 // indirect github.com/nishanths/predeclared v0.2.2 // indirect github.com/nunnatsa/ginkgolinter v0.16.2 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/polyfloyd/go-errorlint v1.5.2 // indirect + github.com/polyfloyd/go-errorlint v1.6.0 // indirect github.com/prometheus/client_golang v1.12.1 // indirect github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.7.3 // indirect - github.com/quasilyte/go-ruleguard v0.4.2 // indirect + github.com/quasilyte/go-ruleguard v0.4.3-0.20240823090925-0fe6f58b47b1 // indirect github.com/quasilyte/go-ruleguard/dsl v0.3.22 // indirect github.com/quasilyte/gogrep v0.5.0 // indirect github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 // indirect github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect - github.com/ryancurrah/gomodguard v1.3.2 // indirect + github.com/ryancurrah/gomodguard v1.3.5 // indirect github.com/ryanrolds/sqlclosecheck v0.5.1 // indirect github.com/sanposhiho/wastedassign/v2 v2.0.7 // indirect github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect github.com/sashamelentyev/interfacebloat v1.1.0 // indirect - github.com/sashamelentyev/usestdlibvars v1.26.0 // indirect - github.com/securego/gosec/v2 v2.20.1-0.20240525090044-5f0084eb01a9 // indirect + github.com/sashamelentyev/usestdlibvars v1.27.0 // indirect + github.com/securego/gosec/v2 v2.21.2 // indirect github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/sivchari/containedctx v1.0.3 // indirect - github.com/sivchari/tenv v1.7.1 // indirect + github.com/sivchari/tenv v1.10.0 // indirect github.com/sonatard/noctx v0.0.2 // indirect github.com/sourcegraph/go-diff v0.7.0 // indirect github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.5.0 // indirect - github.com/spf13/cobra v1.7.0 // indirect + github.com/spf13/cobra v1.8.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/viper v1.12.0 // indirect @@ -156,39 +156,37 @@ require ( github.com/stretchr/objx v0.5.2 // indirect github.com/stretchr/testify v1.9.0 // indirect github.com/subosito/gotenv v1.4.1 // indirect - github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c // indirect github.com/tdakkota/asciicheck v0.2.0 // indirect - github.com/tetafro/godot v1.4.16 // indirect + github.com/tetafro/godot v1.4.17 // indirect github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966 // indirect github.com/timonwong/loggercheck v0.9.4 // indirect - github.com/tomarrell/wrapcheck/v2 v2.8.3 // indirect + github.com/tomarrell/wrapcheck/v2 v2.9.0 // indirect github.com/tommy-muehle/go-mnd/v2 v2.5.1 // indirect github.com/ultraware/funlen v0.1.0 // indirect github.com/ultraware/whitespace v0.1.1 // indirect - github.com/uudashr/gocognit v1.1.2 // indirect + github.com/uudashr/gocognit v1.1.3 // indirect github.com/xen0n/gosmopolitan v1.2.2 // indirect github.com/yagipy/maintidx v1.0.0 // indirect github.com/yeya24/promlinter v0.3.0 // indirect github.com/ykadowak/zerologlint v0.1.5 // indirect gitlab.com/bosi/decorder v0.4.2 // indirect go-simpler.org/musttag v0.12.2 // indirect - go-simpler.org/sloglint v0.7.1 // indirect + go-simpler.org/sloglint v0.7.2 // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/automaxprocs v1.5.3 // indirect go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.24.0 // indirect - golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc // indirect + golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e // indirect golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f // indirect - golang.org/x/mod v0.20.0 // indirect + golang.org/x/mod v0.21.0 // indirect golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.23.0 // indirect - golang.org/x/text v0.15.0 // indirect - google.golang.org/protobuf v1.33.0 // indirect - gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect + golang.org/x/sys v0.25.0 // indirect + golang.org/x/text v0.18.0 // indirect + google.golang.org/protobuf v1.34.2 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - honnef.co/go/tools v0.4.7 // indirect - mvdan.cc/gofumpt v0.6.0 // indirect + honnef.co/go/tools v0.5.1 // indirect + mvdan.cc/gofumpt v0.7.0 // indirect mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f // indirect ) diff --git a/tools/go.sum b/tools/go.sum index 9b133a50d7..022da35dcf 100644 --- a/tools/go.sum +++ b/tools/go.sum @@ -37,26 +37,26 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/4meepo/tagalign v1.3.4 h1:P51VcvBnf04YkHzjfclN6BbsopfJR5rxs1n+5zHt+w8= github.com/4meepo/tagalign v1.3.4/go.mod h1:M+pnkHH2vG8+qhE5bVc/zeP7HS/j910Fwa9TUSyZVI0= -github.com/Abirdcfly/dupword v0.0.14 h1:3U4ulkc8EUo+CaT105/GJ1BQwtgyj6+VaBVbAX11Ba8= -github.com/Abirdcfly/dupword v0.0.14/go.mod h1:VKDAbxdY8YbKUByLGg8EETzYSuC4crm9WwI6Y3S0cLI= +github.com/Abirdcfly/dupword v0.1.1 h1:Bsxe0fIw6OwBtXMIncaTxCLHYO5BB+3mcsR5E8VXloY= +github.com/Abirdcfly/dupword v0.1.1/go.mod h1:B49AcJdTYYkpd4HjgAcutNGG9HZ2JWwKunH9Y2BA6sM= github.com/Antonboom/errname v0.1.13 h1:JHICqsewj/fNckzrfVSe+T33svwQxmjC+1ntDsHOVvM= github.com/Antonboom/errname v0.1.13/go.mod h1:uWyefRYRN54lBg6HseYCFhs6Qjcy41Y3Jl/dVhA87Ns= github.com/Antonboom/nilnil v0.1.9 h1:eKFMejSxPSA9eLSensFmjW2XTgTwJMjZ8hUHtV4s/SQ= github.com/Antonboom/nilnil v0.1.9/go.mod h1:iGe2rYwCq5/Me1khrysB4nwI7swQvjclR8/YRPl5ihQ= -github.com/Antonboom/testifylint v1.3.1 h1:Uam4q1Q+2b6H7gvk9RQFw6jyVDdpzIirFOOrbs14eG4= -github.com/Antonboom/testifylint v1.3.1/go.mod h1:NV0hTlteCkViPW9mSR4wEMfwp+Hs1T3dY60bkvSfhpM= +github.com/Antonboom/testifylint v1.4.3 h1:ohMt6AHuHgttaQ1xb6SSnxCeK4/rnK7KKzbvs7DmEck= +github.com/Antonboom/testifylint v1.4.3/go.mod h1:+8Q9+AOLsz5ZiQiiYujJKs9mNz398+M6UgslP4qgJLA= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= -github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs= +github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/Crocmagnon/fatcontext v0.2.2 h1:OrFlsDdOj9hW/oBEJBNSuH7QWf+E9WPVHw+x52bXVbk= -github.com/Crocmagnon/fatcontext v0.2.2/go.mod h1:WSn/c/+MMNiD8Pri0ahRj0o9jVpeowzavOQplBJw6u0= +github.com/Crocmagnon/fatcontext v0.5.2 h1:vhSEg8Gqng8awhPju2w7MKHqMlg4/NI+gSDHtR3xgwA= +github.com/Crocmagnon/fatcontext v0.5.2/go.mod h1:87XhRMaInHP44Q7Tlc7jkgKKB7kZAOPiDkFMdKCC+74= github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 h1:sHglBQTwgx+rWPdisA5ynNEsoARbiCBOyGcJM4/OzsM= github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= -github.com/GaijinEntertainment/go-exhaustruct/v3 v3.2.0 h1:sATXp1x6/axKxz2Gjxv8MALP0bXaNRfQinEwyfMcx8c= -github.com/GaijinEntertainment/go-exhaustruct/v3 v3.2.0/go.mod h1:Nl76DrGNJTA1KJ0LePKBw/vznBX1EHbAZX8mwjR82nI= -github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= -github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0 h1:/fTUt5vmbkAcMBt4YQiuC23cV0kEsN1MVMNqeOW43cU= +github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0/go.mod h1:ONJg5sxcbsdQQ4pOW8TGdTidT2TMAUy/2Xhr8mrYaao= +github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= +github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/OpenPeeDeeP/depguard/v2 v2.2.0 h1:vDfG60vDtIuf0MEOhmLlLLSzqaRM8EMcgJPdp74zmpA= github.com/OpenPeeDeeP/depguard/v2 v2.2.0/go.mod h1:CIzddKRvLBC4Au5aYP/i3nyaWQ+ClszLIuVocRiCYFQ= github.com/alecthomas/assert/v2 v2.2.2 h1:Z/iVC0xZfWTaFNE6bA3z07T86hd45Xe2eLt6WVy2bbk= @@ -90,8 +90,8 @@ github.com/bkielbasa/cyclop v1.2.1 h1:AeF71HZDob1P2/pRm1so9cd1alZnrpyc4q2uP2l0gJ github.com/bkielbasa/cyclop v1.2.1/go.mod h1:K/dT/M0FPAiYjBgQGau7tz+3TMh4FWAEqlMhzFWCrgM= github.com/blizzy78/varnamelen v0.8.0 h1:oqSblyuQvFsW1hbBHh1zfwrKe3kcSj0rnXkKzsQ089M= github.com/blizzy78/varnamelen v0.8.0/go.mod h1:V9TzQZ4fLJ1DSrjVDfl89H7aMnTvKkApdHeyESmyR7k= -github.com/bombsimon/wsl/v4 v4.2.1 h1:Cxg6u+XDWff75SIFFmNsqnIOgob+Q9hG6y/ioKbRFiM= -github.com/bombsimon/wsl/v4 v4.2.1/go.mod h1:Xu/kDxGZTofQcDGCtQe9KCzhHphIe0fDuyWTxER9Feo= +github.com/bombsimon/wsl/v4 v4.4.1 h1:jfUaCkN+aUpobrMO24zwyAMwMAV5eSziCkOKEauOLdw= +github.com/bombsimon/wsl/v4 v4.4.1/go.mod h1:Xu/kDxGZTofQcDGCtQe9KCzhHphIe0fDuyWTxER9Feo= github.com/breml/bidichk v0.2.7 h1:dAkKQPLl/Qrk7hnP6P+E0xOodrq8Us7+U0o4UBOAlQY= github.com/breml/bidichk v0.2.7/go.mod h1:YodjipAGI9fGcYM7II6wFvGhdMYsC5pHDlGzqvEW3tQ= github.com/breml/errchkjson v0.3.6 h1:VLhVkqSBH96AvXEyclMR37rZslRrY2kcyq+31HCsVrA= @@ -117,15 +117,15 @@ github.com/chavacava/garif v0.1.0/go.mod h1:XMyYCkEL58DF0oyW4qDjjnPWONs2HBqYKI+U github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/ckaznocha/intrange v0.1.2 h1:3Y4JAxcMntgb/wABQ6e8Q8leMd26JbX2790lIss9MTI= -github.com/ckaznocha/intrange v0.1.2/go.mod h1:RWffCw/vKBwHeOEwWdCikAtY0q4gGt8VhJZEEA5n+RE= +github.com/ckaznocha/intrange v0.2.0 h1:FykcZuJ8BD7oX93YbO1UY9oZtkRbp+1/kJcDjkefYLs= +github.com/ckaznocha/intrange v0.2.0/go.mod h1:r5I7nUlAAG56xmkOpw4XVr16BXhwYTUdcuRFeevn1oE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/curioswitch/go-reassign v0.2.0 h1:G9UZyOcpk/d7Gd6mqYgd8XYWFMw/znxwGDUstnC9DIo= github.com/curioswitch/go-reassign v0.2.0/go.mod h1:x6OpXuWvgfQaMGks2BZybTngWjT84hqJfKoO8Tt/Roc= -github.com/daixiang0/gci v0.13.4 h1:61UGkmpoAcxHM2hhNkZEf5SzwQtWJXTSws7jaPyqwlw= -github.com/daixiang0/gci v0.13.4/go.mod h1:12etP2OniiIdP4q+kjUGrC/rUagga7ODbqsom5Eo5Yk= +github.com/daixiang0/gci v0.13.5 h1:kThgmH1yBmZSBCh1EJVxQ7JsHpm5Oms0AMed/0LaH4c= +github.com/daixiang0/gci v0.13.5/go.mod h1:12etP2OniiIdP4q+kjUGrC/rUagga7ODbqsom5Eo5Yk= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -143,8 +143,8 @@ github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4 github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/firefart/nonamedreturns v1.0.5 h1:tM+Me2ZaXs8tfdDw3X6DOX++wMCOqzYUho6tUTYIdRA= github.com/firefart/nonamedreturns v1.0.5/go.mod h1:gHJjDqhGM4WyPt639SOZs+G89Ko7QKH5R5BhnO6xJhw= -github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= -github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo= @@ -162,8 +162,10 @@ github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vb github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= -github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= +github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= @@ -186,14 +188,14 @@ github.com/go-toolsmith/strparse v1.1.0 h1:GAioeZUK9TGxnLS+qfdqNbA4z0SSm5zVNtCQi github.com/go-toolsmith/strparse v1.1.0/go.mod h1:7ksGy58fsaQkGQlY8WVoBFNyEPMGuJin1rfoPS4lBSQ= github.com/go-toolsmith/typep v1.1.0 h1:fIRYDyF+JywLfqzyhdiHzRop/GQDxxNhLGQ6gFUNHus= github.com/go-toolsmith/typep v1.1.0/go.mod h1:fVIw+7zjdsMxDA3ITWnH1yOiw1rnTQKCsF/sk2H/qig= -github.com/go-viper/mapstructure/v2 v2.0.0 h1:dhn8MZ1gZ0mzeodTG3jt5Vj/o87xZKuNAprG2mQfMfc= -github.com/go-viper/mapstructure/v2 v2.0.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/go-viper/mapstructure/v2 v2.1.0 h1:gHnMa2Y/pIxElCH2GlZZ1lZSsn6XMtufpGyP1XxdC/w= +github.com/go-viper/mapstructure/v2 v2.1.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/go-xmlfmt/xmlfmt v1.1.2 h1:Nea7b4icn8s57fTx1M5AI4qQT5HEM3rVUO8MuE6g80U= github.com/go-xmlfmt/xmlfmt v1.1.2/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= -github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= -github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= +github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -226,10 +228,10 @@ github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM= github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk= -github.com/golangci/gofmt v0.0.0-20231018234816-f50ced29576e h1:ULcKCDV1LOZPFxGZaA6TlQbiM3J2GCPnkx/bGF6sX/g= -github.com/golangci/gofmt v0.0.0-20231018234816-f50ced29576e/go.mod h1:Pm5KhLPA8gSnQwrQ6ukebRcapGb/BG9iUkdaiCcGHJM= -github.com/golangci/golangci-lint v1.59.1 h1:CRRLu1JbhK5avLABFJ/OHVSQ0Ie5c4ulsOId1h3TTks= -github.com/golangci/golangci-lint v1.59.1/go.mod h1:jX5Oif4C7P0j9++YB2MMJmoNrb01NJ8ITqKWNLewThg= +github.com/golangci/gofmt v0.0.0-20240816233607-d8596aa466a9 h1:/1322Qns6BtQxUZDTAT4SdcoxknUki7IAoK4SAXr8ME= +github.com/golangci/gofmt v0.0.0-20240816233607-d8596aa466a9/go.mod h1:Oesb/0uFAyWoaw1U1qS5zyjCg5NP9C9iwjnI4tIsXEE= +github.com/golangci/golangci-lint v1.61.0 h1:VvbOLaRVWmyxCnUIMTbf1kDsaJbTzH20FAMXTAlQGu8= +github.com/golangci/golangci-lint v1.61.0/go.mod h1:e4lztIrJJgLPhWvFPDkhiMwEFRrWlmFbrZea3FsJyN8= github.com/golangci/misspell v0.6.0 h1:JCle2HUTNWirNlDIAUO44hUsKhOFqGPoC4LZxlaSXDs= github.com/golangci/misspell v0.6.0/go.mod h1:keMNyY6R9isGaSAu+4Q8NMBwMPkh15Gtc8UCVoDtAWo= github.com/golangci/modinfo v0.3.4 h1:oU5huX3fbxqQXdfspamej74DFX0kyGLkw1ppvXoJ8GA= @@ -266,8 +268,8 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg= -github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= +github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 h1:5iH8iuqE5apketRbSFBy+X1V0o+l+8NF1avt4HWl7cA= +github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= @@ -303,8 +305,8 @@ github.com/jingyugao/rowserrcheck v1.1.1 h1:zibz55j/MJtLsjP1OF4bSdgXxwL1b+Vn7Tjz github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c= github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af h1:KA9BjwUk7KlCh6S9EAGWBt1oExIUv9WyNCiRz5amv48= github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0= -github.com/jjti/go-spancheck v0.6.1 h1:ZK/wE5Kyi1VX3PJpUO2oEgeoI4FWOUm7Shb2Gbv5obI= -github.com/jjti/go-spancheck v0.6.1/go.mod h1:vF1QkOO159prdo6mHRxak2CpzDpHAfKiPUDP/NeRnX8= +github.com/jjti/go-spancheck v0.6.2 h1:iYtoxqPMzHUPp7St+5yA8+cONdyXD3ug6KK15n7Pklk= +github.com/jjti/go-spancheck v0.6.2/go.mod h1:+X7lvIrR5ZdUTkxFYqzJ0abr8Sb5LOo80uOhWNqIrYA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -370,8 +372,8 @@ github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/Qd github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mgechev/revive v1.3.7 h1:502QY0vQGe9KtYJ9FpxMz9rL+Fc/P13CI5POL4uHCcE= -github.com/mgechev/revive v1.3.7/go.mod h1:RJ16jUbF0OWC3co/+XTxmFNgEpUPwnnA0BRllX2aDNA= +github.com/mgechev/revive v1.3.9 h1:18Y3R4a2USSBF+QZKFQwVkBROUda7uoBlkEuBD+YD1A= +github.com/mgechev/revive v1.3.9/go.mod h1:+uxEIr5UH0TjXWHTno3xh4u7eg6jDpXKzQccA9UGhHU= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= @@ -381,14 +383,12 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/moricho/tparallel v0.3.1 h1:fQKD4U1wRMAYNngDonW5XupoB/ZGJHdpzrWqgyg9krA= -github.com/moricho/tparallel v0.3.1/go.mod h1:leENX2cUv7Sv2qDgdi0D0fCftN8fRC67Bcn8pqzeYNI= +github.com/moricho/tparallel v0.3.2 h1:odr8aZVFA3NZrNybggMkYO3rgPRcqjeQUlBBFVxKHTI= +github.com/moricho/tparallel v0.3.2/go.mod h1:OQ+K3b4Ln3l2TZveGCywybl68glfLEwFGqvnjok8b+U= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nakabonne/nestif v0.3.1 h1:wm28nZjhQY5HyYPx+weN3Q65k6ilSBxDb8v5S81B81U= github.com/nakabonne/nestif v0.3.1/go.mod h1:9EtoZochLn5iUprVDmDjqGKPofoUEBL8U4Ngq6aY7OE= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nishanths/exhaustive v0.12.0 h1:vIY9sALmw6T/yxiASewa4TQcFsVYZQQRUQJhKRf3Swg= github.com/nishanths/exhaustive v0.12.0/go.mod h1:mEZ95wPIZW+x8kC4TgC+9YCUgiST7ecevsVDTgc2obs= github.com/nishanths/predeclared v0.2.2 h1:V2EPdZPliZymNAn79T8RkNApBjMmVKh5XRpLm/w98Vk= @@ -397,10 +397,10 @@ github.com/nunnatsa/ginkgolinter v0.16.2 h1:8iLqHIZvN4fTLDC0Ke9tbSZVcyVHoBs0HIbn github.com/nunnatsa/ginkgolinter v0.16.2/go.mod h1:4tWRinDN1FeJgU+iJANW/kz7xKN5nYRAOfJDQUS9dOQ= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/onsi/ginkgo/v2 v2.17.3 h1:oJcvKpIb7/8uLpDDtnQuf18xVnwKp8DTD7DQ6gTd/MU= -github.com/onsi/ginkgo/v2 v2.17.3/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc= -github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= -github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= +github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4= +github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag= +github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8= +github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc= github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU= github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w= @@ -410,16 +410,16 @@ github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT9 github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= -github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= +github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/polyfloyd/go-errorlint v1.5.2 h1:SJhVik3Umsjh7mte1vE0fVZ5T1gznasQG3PV7U5xFdA= -github.com/polyfloyd/go-errorlint v1.5.2/go.mod h1:sH1QC1pxxi0fFecsVIzBmxtrgd9IF/SkJpA6wqyKAJs= +github.com/polyfloyd/go-errorlint v1.6.0 h1:tftWV9DE7txiFzPpztTAwyoRLKNj9gpVm2cg8/OwcYY= +github.com/polyfloyd/go-errorlint v1.6.0/go.mod h1:HR7u8wuP1kb1NeN1zqTd1ZMlqUKPPHF+Id4vIPvDqVw= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -444,8 +444,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/quasilyte/go-ruleguard v0.4.2 h1:htXcXDK6/rO12kiTHKfHuqR4kr3Y4M0J0rOL6CH/BYs= -github.com/quasilyte/go-ruleguard v0.4.2/go.mod h1:GJLgqsLeo4qgavUoL8JeGFNS7qcisx3awV/w9eWTmNI= +github.com/quasilyte/go-ruleguard v0.4.3-0.20240823090925-0fe6f58b47b1 h1:+Wl/0aFp0hpuHM3H//KMft64WQ1yX9LdJY64Qm/gFCo= +github.com/quasilyte/go-ruleguard v0.4.3-0.20240823090925-0fe6f58b47b1/go.mod h1:GJLgqsLeo4qgavUoL8JeGFNS7qcisx3awV/w9eWTmNI= github.com/quasilyte/go-ruleguard/dsl v0.3.22 h1:wd8zkOhSNr+I+8Qeciml08ivDt1pSXe60+5DqOpCjPE= github.com/quasilyte/go-ruleguard/dsl v0.3.22/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= github.com/quasilyte/gogrep v0.5.0 h1:eTKODPXbI8ffJMN+W2aE0+oL0z/nh8/5eNdiO34SOAo= @@ -460,8 +460,8 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryancurrah/gomodguard v1.3.2 h1:CuG27ulzEB1Gu5Dk5gP8PFxSOZ3ptSdP5iI/3IXxM18= -github.com/ryancurrah/gomodguard v1.3.2/go.mod h1:LqdemiFomEjcxOqirbQCb3JFvSxH2JUYMerTFd3sF2o= +github.com/ryancurrah/gomodguard v1.3.5 h1:cShyguSwUEeC0jS7ylOiG/idnd1TpJ1LfHGpV3oJmPU= +github.com/ryancurrah/gomodguard v1.3.5/go.mod h1:MXlEPQRxgfPQa62O8wzK3Ozbkv9Rkqr+wKjSxTdsNJE= github.com/ryanrolds/sqlclosecheck v0.5.1 h1:dibWW826u0P8jNLsLN+En7+RqWWTYrjCB9fJfSfdyCU= github.com/ryanrolds/sqlclosecheck v0.5.1/go.mod h1:2g3dUjoS6AL4huFdv6wn55WpLIDjY7ZgUR4J8HOO/XQ= github.com/sanposhiho/wastedassign/v2 v2.0.7 h1:J+6nrY4VW+gC9xFzUc+XjPD3g3wF3je/NsJFwFK7Uxc= @@ -470,10 +470,10 @@ github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6Ng github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= github.com/sashamelentyev/interfacebloat v1.1.0 h1:xdRdJp0irL086OyW1H/RTZTr1h/tMEOsumirXcOJqAw= github.com/sashamelentyev/interfacebloat v1.1.0/go.mod h1:+Y9yU5YdTkrNvoX0xHc84dxiN1iBi9+G8zZIhPVoNjQ= -github.com/sashamelentyev/usestdlibvars v1.26.0 h1:LONR2hNVKxRmzIrZR0PhSF3mhCAzvnr+DcUiHgREfXE= -github.com/sashamelentyev/usestdlibvars v1.26.0/go.mod h1:9nl0jgOfHKWNFS43Ojw0i7aRoS4j6EBye3YBhmAIRF8= -github.com/securego/gosec/v2 v2.20.1-0.20240525090044-5f0084eb01a9 h1:rnO6Zp1YMQwv8AyxzuwsVohljJgp4L0ZqiCgtACsPsc= -github.com/securego/gosec/v2 v2.20.1-0.20240525090044-5f0084eb01a9/go.mod h1:dg7lPlu/xK/Ut9SedURCoZbVCR4yC7fM65DtH9/CDHs= +github.com/sashamelentyev/usestdlibvars v1.27.0 h1:t/3jZpSXtRPRf2xr0m63i32ZrusyurIGT9E5wAvXQnI= +github.com/sashamelentyev/usestdlibvars v1.27.0/go.mod h1:9nl0jgOfHKWNFS43Ojw0i7aRoS4j6EBye3YBhmAIRF8= +github.com/securego/gosec/v2 v2.21.2 h1:deZp5zmYf3TWwU7A7cR2+SolbTpZ3HQiwFqnzQyEl3M= +github.com/securego/gosec/v2 v2.21.2/go.mod h1:au33kg78rNseF5PwPnTWhuYBFf534bvJRvOrgZ/bFzU= github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c h1:W65qqJCIOVP4jpqPQ0YvHYKwcMEMVWIzWC5iNQQfBTU= github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs= github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= @@ -485,8 +485,8 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sivchari/containedctx v1.0.3 h1:x+etemjbsh2fB5ewm5FeLNi5bUjK0V8n0RB+Wwfd0XE= github.com/sivchari/containedctx v1.0.3/go.mod h1:c1RDvCbnJLtH4lLcYD/GqwiBSSf4F5Qk0xld2rBqzJ4= -github.com/sivchari/tenv v1.7.1 h1:PSpuD4bu6fSmtWMxSGWcvqUUgIn7k3yOJhOIzVWn8Ak= -github.com/sivchari/tenv v1.7.1/go.mod h1:64yStXKSOxDfX47NlhVwND4dHwfZDdbp2Lyl018Icvg= +github.com/sivchari/tenv v1.10.0 h1:g/hzMA+dBCKqGXgW8AV/1xIWhAvDrx0zFKNR48NFMg0= +github.com/sivchari/tenv v1.10.0/go.mod h1:tdY24masnVoZFxYrHv/nD6Tc8FbkEtAQEEziXpyMgqY= github.com/sonatard/noctx v0.0.2 h1:L7Dz4De2zDQhW8S0t+KUjY0MAQJd6SgVwhzNIc4ok00= github.com/sonatard/noctx v0.0.2/go.mod h1:kzFz+CzWSjQ2OzIm46uJZoXuBpa2+0y3T36U18dWqIo= github.com/sourcegraph/go-diff v0.7.0 h1:9uLlrd5T46OXs5qpp8L/MTltk0zikUGi0sNNyCpA8G0= @@ -495,8 +495,8 @@ github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= -github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -525,30 +525,28 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= -github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c h1:+aPplBwWcHBo6q9xrfWdMrT9o4kltkmmvpemgIjep/8= -github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c/go.mod h1:SbErYREK7xXdsRiigaQiQkI9McGRzYMvlKYaP3Nimdk= github.com/tdakkota/asciicheck v0.2.0 h1:o8jvnUANo0qXtnslk2d3nMKTFNlOnJjRrNcj0j9qkHM= github.com/tdakkota/asciicheck v0.2.0/go.mod h1:Qb7Y9EgjCLJGup51gDHFzbI08/gbGhL/UVhYIPWG2rg= github.com/tenntenn/modver v1.0.1 h1:2klLppGhDgzJrScMpkj9Ujy3rXPUspSjAcev9tSEBgA= github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0= github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3 h1:f+jULpRQGxTSkNYKJ51yaw6ChIqO+Je8UqsTKN/cDag= github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY= -github.com/tetafro/godot v1.4.16 h1:4ChfhveiNLk4NveAZ9Pu2AN8QZ2nkUGFuadM9lrr5D0= -github.com/tetafro/godot v1.4.16/go.mod h1:2oVxTBSftRTh4+MVfUaUXR6bn2GDXCaMcOG4Dk3rfio= +github.com/tetafro/godot v1.4.17 h1:pGzu+Ye7ZUEFx7LHU0dAKmCOXWsPjl7qA6iMGndsjPs= +github.com/tetafro/godot v1.4.17/go.mod h1:2oVxTBSftRTh4+MVfUaUXR6bn2GDXCaMcOG4Dk3rfio= github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966 h1:quvGphlmUVU+nhpFa4gg4yJyTRJ13reZMDHrKwYw53M= github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966/go.mod h1:27bSVNWSBOHm+qRp1T9qzaIpsWEP6TbUnei/43HK+PQ= github.com/timonwong/loggercheck v0.9.4 h1:HKKhqrjcVj8sxL7K77beXh0adEm6DLjV/QOGeMXEVi4= github.com/timonwong/loggercheck v0.9.4/go.mod h1:caz4zlPcgvpEkXgVnAJGowHAMW2NwHaNlpS8xDbVhTg= -github.com/tomarrell/wrapcheck/v2 v2.8.3 h1:5ov+Cbhlgi7s/a42BprYoxsr73CbdMUTzE3bRDFASUs= -github.com/tomarrell/wrapcheck/v2 v2.8.3/go.mod h1:g9vNIyhb5/9TQgumxQyOEqDHsmGYcGsVMOx/xGkqdMo= +github.com/tomarrell/wrapcheck/v2 v2.9.0 h1:801U2YCAjLhdN8zhZ/7tdjB3EnAoRlJHt/s+9hijLQ4= +github.com/tomarrell/wrapcheck/v2 v2.9.0/go.mod h1:g9vNIyhb5/9TQgumxQyOEqDHsmGYcGsVMOx/xGkqdMo= github.com/tommy-muehle/go-mnd/v2 v2.5.1 h1:NowYhSdyE/1zwK9QCLeRb6USWdoif80Ie+v+yU8u1Zw= github.com/tommy-muehle/go-mnd/v2 v2.5.1/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw= github.com/ultraware/funlen v0.1.0 h1:BuqclbkY6pO+cvxoq7OsktIXZpgBSkYTQtmwhAK81vI= github.com/ultraware/funlen v0.1.0/go.mod h1:XJqmOQja6DpxarLj6Jj1U7JuoS8PvL4nEqDaQhy22p4= github.com/ultraware/whitespace v0.1.1 h1:bTPOGejYFulW3PkcrqkeQwOd6NKOOXvmGD9bo/Gk8VQ= github.com/ultraware/whitespace v0.1.1/go.mod h1:XcP1RLD81eV4BW8UhQlpaR+SDc2givTvyI8a586WjW8= -github.com/uudashr/gocognit v1.1.2 h1:l6BAEKJqQH2UpKAPKdMfZf5kE4W/2xk8pfU1OVLvniI= -github.com/uudashr/gocognit v1.1.2/go.mod h1:aAVdLURqcanke8h3vg35BC++eseDm66Z7KmchI5et4k= +github.com/uudashr/gocognit v1.1.3 h1:l+a111VcDbKfynh+airAy/DJQKaXh2m9vkoysMPSZyM= +github.com/uudashr/gocognit v1.1.3/go.mod h1:aKH8/e8xbTRBwjbCkwZ8qt4l2EpKXl31KMHgSS+lZ2U= github.com/xen0n/gosmopolitan v1.2.2 h1:/p2KTnMzwRexIW8GlKawsTWOxn7UHA+jCMF/V8HHtvU= github.com/xen0n/gosmopolitan v1.2.2/go.mod h1:7XX7Mj61uLYrj0qmeN0zi7XDon9JRAEhYQqAPLVNTeg= github.com/yagipy/maintidx v1.0.0 h1:h5NvIsCz+nRDapQ0exNv4aJ0yXSI0420omVANTv3GJM= @@ -570,8 +568,8 @@ go-simpler.org/assert v0.9.0 h1:PfpmcSvL7yAnWyChSjOz6Sp6m9j5lyK8Ok9pEL31YkQ= go-simpler.org/assert v0.9.0/go.mod h1:74Eqh5eI6vCK6Y5l3PI8ZYFXG4Sa+tkr70OIPJAUr28= go-simpler.org/musttag v0.12.2 h1:J7lRc2ysXOq7eM8rwaTYnNrHd5JwjppzB6mScysB2Cs= go-simpler.org/musttag v0.12.2/go.mod h1:uN1DVIasMTQKk6XSik7yrJoEysGtR2GRqvWnI9S7TYM= -go-simpler.org/sloglint v0.7.1 h1:qlGLiqHbN5islOxjeLXoPtUdZXb669RW+BDQ+xOSNoU= -go-simpler.org/sloglint v0.7.1/go.mod h1:OlaVDRh/FKKd4X4sIMbsz8st97vomydceL146Fthh/c= +go-simpler.org/sloglint v0.7.2 h1:Wc9Em/Zeuu7JYpl+oKoYOsQSy2X560aVueCW/m6IijY= +go-simpler.org/sloglint v0.7.2/go.mod h1:US+9C80ppl7VsThQclkM7BkCHQAzuz8kHLsW3ppuluo= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -605,8 +603,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc h1:ao2WRsKSzW6KuUY9IWPwWahcHCgR0s52IfwutMfEbdM= -golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= +golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e h1:I88y4caeGeuDQxgdoFPUq097j7kNfw6uvuiNxUBfcBk= +golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f h1:phY1HzDcf18Aq9A8KkmRtY9WvOFIxN8wgfvy6Zm1DV8= @@ -639,8 +637,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= -golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= +golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -747,7 +745,6 @@ golang.org/x/sys v0.0.0-20211105183446-c75c47738b0c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220702020025-31831981b65f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -756,8 +753,8 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= -golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -774,8 +771,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -834,7 +831,6 @@ golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= -golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= @@ -921,14 +917,14 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= @@ -949,10 +945,10 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.4.7 h1:9MDAWxMoSnB6QoSqiVr7P5mtkT9pOc1kSxchzPCnqJs= -honnef.co/go/tools v0.4.7/go.mod h1:+rnGS1THNh8zMwnd2oVOTL9QF6vmfyG6ZXBULae2uc0= -mvdan.cc/gofumpt v0.6.0 h1:G3QvahNDmpD+Aek/bNOLrFR2XC6ZAdo62dZu65gmwGo= -mvdan.cc/gofumpt v0.6.0/go.mod h1:4L0wf+kgIPZtcCWXynNS2e6bhmj73umwnuXSZarixzA= +honnef.co/go/tools v0.5.1 h1:4bH5o3b5ZULQ4UrBmP+63W9r7qIkqJClEA9ko5YKx+I= +honnef.co/go/tools v0.5.1/go.mod h1:e9irvo83WDG9/irijV44wr3tbhcFeRnfpVlRqVwpzMs= +mvdan.cc/gofumpt v0.7.0 h1:bg91ttqXmi9y2xawvkuMXyvAA/1ZGJqYAEGjXuP0JXU= +mvdan.cc/gofumpt v0.7.0/go.mod h1:txVFJy/Sc/mvaycET54pV8SW8gWxTlUuGHVEcncmNUo= mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f h1:lMpcwN6GxNbWtbpI1+xzFLSW8XzX0u72NttUGVFjO3U= mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f/go.mod h1:RSLa7mKKCNeTTMHBw5Hsy2rfJmd6O2ivt9Dw9ZqCQpQ= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/tools/vendor/github.com/Abirdcfly/dupword/dupword.go b/tools/vendor/github.com/Abirdcfly/dupword/dupword.go index 9a78fb6cca..6838d7e759 100644 --- a/tools/vendor/github.com/Abirdcfly/dupword/dupword.go +++ b/tools/vendor/github.com/Abirdcfly/dupword/dupword.go @@ -128,7 +128,12 @@ func (a *analyzer) run(pass *analysis.Pass) (interface{}, error) { } func (a *analyzer) fixDuplicateWordInComment(pass *analysis.Pass, f *ast.File) { + isTestFile := strings.HasSuffix(pass.Fset.File(f.FileStart).Name(), "_test.go") for _, cg := range f.Comments { + // avoid checking example outputs for duplicate words + if isTestFile && isExampleOutputStart(cg.List[0].Text) { + continue + } var preLine *ast.Comment for _, c := range cg.List { update, keyword, find := a.Check(c.Text) @@ -329,3 +334,10 @@ func ExcludeWords(word string) (exclude bool) { } return false } + +func isExampleOutputStart(comment string) bool { + return strings.HasPrefix(comment, "// Output:") || + strings.HasPrefix(comment, "// output:") || + strings.HasPrefix(comment, "// Unordered output:") || + strings.HasPrefix(comment, "// unordered output:") +} diff --git a/tools/vendor/github.com/Antonboom/testifylint/analyzer/checkers_factory.go b/tools/vendor/github.com/Antonboom/testifylint/analyzer/checkers_factory.go index fcab8e3117..df04dfdc5f 100644 --- a/tools/vendor/github.com/Antonboom/testifylint/analyzer/checkers_factory.go +++ b/tools/vendor/github.com/Antonboom/testifylint/analyzer/checkers_factory.go @@ -55,6 +55,10 @@ func newCheckers(cfg config.Config) ([]checkers.RegularChecker, []checkers.Advan case *checkers.ExpectedActual: c.SetExpVarPattern(cfg.ExpectedActual.ExpVarPattern.Regexp) + case *checkers.Formatter: + c.SetCheckFormatString(cfg.Formatter.CheckFormatString) + c.SetRequireFFuncs(cfg.Formatter.RequireFFuncs) + case *checkers.GoRequire: c.SetIgnoreHTTPHandlers(cfg.GoRequire.IgnoreHTTPHandlers) diff --git a/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/call_meta.go b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/call_meta.go index 94623da150..96b5b19b09 100644 --- a/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/call_meta.go +++ b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/call_meta.go @@ -6,6 +6,7 @@ import ( "strings" "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/types/typeutil" "github.com/Antonboom/testifylint/internal/analysisutil" "github.com/Antonboom/testifylint/internal/testify" @@ -15,6 +16,8 @@ import ( // // assert.Equal(t, 42, result, "helpful comment") type CallMeta struct { + // Call stores the original AST call expression. + Call *ast.CallExpr // Range contains start and end position of assertion call. analysis.Range // IsPkg true if this is package (not object) call. @@ -49,6 +52,8 @@ type FnMeta struct { NameFTrimmed string // IsFmt is true if function is formatted, e.g. "Equalf". IsFmt bool + // Signature represents assertion signature. + Signature *types.Signature } // NewCallMeta returns meta information about testify assertion call. @@ -66,16 +71,16 @@ func NewCallMeta(pass *analysis.Pass, ce *ast.CallExpr) *CallMeta { // s.Assert().Equal -> method of *assert.Assertions -> package assert ("vendor/github.com/stretchr/testify/assert") // s.Equal -> method of *assert.Assertions -> package assert ("vendor/github.com/stretchr/testify/assert") // reqObj.Falsef -> method of *require.Assertions -> package require ("vendor/github.com/stretchr/testify/require") - if sel, ok := pass.TypesInfo.Selections[se]; ok { + if sel, isSel := pass.TypesInfo.Selections[se]; isSel { return sel.Obj().Pkg(), false } // Examples: // assert.False -> assert -> package assert ("vendor/github.com/stretchr/testify/assert") // require.NotEqualf -> require -> package require ("vendor/github.com/stretchr/testify/require") - if id, ok := se.X.(*ast.Ident); ok { + if id, isIdent := se.X.(*ast.Ident); isIdent { if selObj := pass.TypesInfo.ObjectOf(id); selObj != nil { - if pkg, ok := selObj.(*types.PkgName); ok { + if pkg, isPkgName := selObj.(*types.PkgName); isPkgName { return pkg.Imported(), true } } @@ -92,7 +97,13 @@ func NewCallMeta(pass *analysis.Pass, ce *ast.CallExpr) *CallMeta { return nil } + funcObj, ok := typeutil.Callee(pass.TypesInfo, ce).(*types.Func) + if !ok { + return nil + } + return &CallMeta{ + Call: ce, Range: ce, IsPkg: isPkgCall, IsAssert: isAssert, @@ -103,6 +114,7 @@ func NewCallMeta(pass *analysis.Pass, ce *ast.CallExpr) *CallMeta { Name: fnName, NameFTrimmed: strings.TrimSuffix(fnName, "f"), IsFmt: strings.HasSuffix(fnName, "f"), + Signature: funcObj.Type().(*types.Signature), // NOTE(a.telyshev): Func's Type() is always a *Signature. }, Args: trimTArg(pass, ce.Args), ArgsRaw: ce.Args, diff --git a/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/checkers_registry.go b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/checkers_registry.go index 84b702b871..17c7d14ee9 100644 --- a/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/checkers_registry.go +++ b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/checkers_registry.go @@ -20,10 +20,13 @@ var registry = checkersRegistry{ {factory: asCheckerFactory(NewSuiteExtraAssertCall), enabledByDefault: true}, {factory: asCheckerFactory(NewSuiteDontUsePkg), enabledByDefault: true}, {factory: asCheckerFactory(NewUselessAssert), enabledByDefault: true}, + {factory: asCheckerFactory(NewFormatter), enabledByDefault: true}, // Advanced checkers. {factory: asCheckerFactory(NewBlankImport), enabledByDefault: true}, {factory: asCheckerFactory(NewGoRequire), enabledByDefault: true}, {factory: asCheckerFactory(NewRequireError), enabledByDefault: true}, + {factory: asCheckerFactory(NewSuiteBrokenParallel), enabledByDefault: true}, + {factory: asCheckerFactory(NewSuiteSubtestRun), enabledByDefault: true}, {factory: asCheckerFactory(NewSuiteTHelper), enabledByDefault: false}, } diff --git a/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/empty.go b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/empty.go index ca7ff41dbb..eafecb678d 100644 --- a/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/empty.go +++ b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/empty.go @@ -21,11 +21,16 @@ import ( // assert.Greater(t, 0, len(arr)) // assert.Less(t, len(arr), 1) // assert.Greater(t, 1, len(arr)) +// assert.Zero(t, len(arr)) +// assert.Empty(t, len(arr)) // // assert.NotEqual(t, 0, len(arr)) // assert.NotEqualValues(t, 0, len(arr)) // assert.Less(t, 0, len(arr)) // assert.Greater(t, len(arr), 0) +// assert.Positive(t, len(arr)) +// assert.NotZero(t, len(arr)) +// assert.NotEmpty(t, len(arr)) // // and requires // @@ -56,10 +61,23 @@ func (checker Empty) checkEmpty(pass *analysis.Pass, call *CallMeta) *analysis.D ) } + if len(call.Args) == 0 { + return nil + } + + a := call.Args[0] + switch call.Fn.NameFTrimmed { + case "Zero", "Empty": + lenArg, ok := isBuiltinLenCall(pass, a) + if ok { + return newUseEmptyDiagnostic(a.Pos(), a.End(), lenArg) + } + } + if len(call.Args) < 2 { return nil } - a, b := call.Args[0], call.Args[1] + b := call.Args[1] switch call.Fn.NameFTrimmed { case "Len": @@ -110,10 +128,23 @@ func (checker Empty) checkNotEmpty(pass *analysis.Pass, call *CallMeta) *analysi ) } + if len(call.Args) == 0 { + return nil + } + + a := call.Args[0] + switch call.Fn.NameFTrimmed { + case "NotZero", "NotEmpty", "Positive": + lenArg, ok := isBuiltinLenCall(pass, a) + if ok { + return newUseNotEmptyDiagnostic(a.Pos(), a.End(), lenArg) + } + } + if len(call.Args) < 2 { return nil } - a, b := call.Args[0], call.Args[1] + b := call.Args[1] switch call.Fn.NameFTrimmed { case "NotEqual", "NotEqualValues": diff --git a/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/expected_actual.go b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/expected_actual.go index bcf89bd201..77784dc7b1 100644 --- a/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/expected_actual.go +++ b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/expected_actual.go @@ -14,7 +14,7 @@ import ( var DefaultExpectedVarPattern = regexp.MustCompile( `(^(exp(ected)?|want(ed)?)([A-Z]\w*)?$)|(^(\w*[a-z])?(Exp(ected)?|Want(ed)?)$)`) -// ExpectedActual detects situation like +// ExpectedActual detects situations like // // assert.Equal(t, result, expected) // assert.EqualExportedValues(t, resultObj, User{Name: "Anton"}) @@ -130,9 +130,9 @@ func (checker ExpectedActual) isExpectedValueCandidate(pass *analysis.Pass, expr return isBasicLit(expr) || isUntypedConst(pass, expr) || isTypedConst(pass, expr) || - isIdentNamedAsExpected(checker.expVarPattern, expr) || - isStructVarNamedAsExpected(checker.expVarPattern, expr) || - isStructFieldNamedAsExpected(checker.expVarPattern, expr) + isIdentNamedAfterPattern(checker.expVarPattern, expr) || + isStructVarNamedAfterPattern(checker.expVarPattern, expr) || + isStructFieldNamedAfterPattern(checker.expVarPattern, expr) } func isParenExpr(ce *ast.CallExpr) bool { @@ -158,7 +158,7 @@ func isCastedBasicLitOrExpectedValue(ce *ast.CallExpr, pattern *regexp.Regexp) b "int", "int8", "int16", "int32", "int64", "float32", "float64", "rune", "string": - return isBasicLit(ce.Args[0]) || isIdentNamedAsExpected(pattern, ce.Args[0]) + return isBasicLit(ce.Args[0]) || isIdentNamedAfterPattern(pattern, ce.Args[0]) } return false } diff --git a/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/float_compare.go b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/float_compare.go index df35674207..7436f9ca1d 100644 --- a/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/float_compare.go +++ b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/float_compare.go @@ -7,7 +7,7 @@ import ( "golang.org/x/tools/go/analysis" ) -// FloatCompare detects situation like +// FloatCompare detects situations like // // assert.Equal(t, 42.42, result) // assert.EqualValues(t, 42.42, result) diff --git a/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/formatter.go b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/formatter.go new file mode 100644 index 0000000000..3401bb0977 --- /dev/null +++ b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/formatter.go @@ -0,0 +1,187 @@ +package checkers + +import ( + "fmt" + "go/ast" + "go/types" + "strconv" + + "golang.org/x/tools/go/analysis" + + "github.com/Antonboom/testifylint/internal/analysisutil" + "github.com/Antonboom/testifylint/internal/checkers/printf" + "github.com/Antonboom/testifylint/internal/testify" +) + +// Formatter detects situations like +// +// assert.ElementsMatch(t, certConfig.Org, csr.Subject.Org, "organizations not equal") +// assert.Error(t, err, fmt.Sprintf("Profile %s should not be valid", test.profile)) +// assert.Errorf(t, err, fmt.Sprintf("test %s", test.testName)) +// assert.Truef(t, targetTs.Equal(ts), "the timestamp should be as expected (%s) but was %s", targetTs) +// ... +// +// and requires +// +// assert.ElementsMatchf(t, certConfig.Org, csr.Subject.Org, "organizations not equal") +// assert.Errorf(t, err, "Profile %s should not be valid", test.profile) +// assert.Errorf(t, err, "test %s", test.testName) +// assert.Truef(t, targetTs.Equal(ts), "the timestamp should be as expected (%s) but was %s", targetTs, ts) +type Formatter struct { + checkFormatString bool + requireFFuncs bool +} + +// NewFormatter constructs Formatter checker. +func NewFormatter() *Formatter { + return &Formatter{ + checkFormatString: true, + requireFFuncs: false, + } +} + +func (Formatter) Name() string { return "formatter" } + +func (checker *Formatter) SetCheckFormatString(v bool) *Formatter { + checker.checkFormatString = v + return checker +} + +func (checker *Formatter) SetRequireFFuncs(v bool) *Formatter { + checker.requireFFuncs = v + return checker +} + +func (checker Formatter) Check(pass *analysis.Pass, call *CallMeta) (result *analysis.Diagnostic) { + if call.Fn.IsFmt { + return checker.checkFmtAssertion(pass, call) + } + return checker.checkNotFmtAssertion(pass, call) +} + +func (checker Formatter) checkNotFmtAssertion(pass *analysis.Pass, call *CallMeta) *analysis.Diagnostic { + msgAndArgsPos, ok := isPrintfLikeCall(pass, call, call.Fn.Signature) + if !ok { + return nil + } + + fFunc := call.Fn.Name + "f" + + if msgAndArgsPos == len(call.ArgsRaw)-1 { + msgAndArgs := call.ArgsRaw[msgAndArgsPos] + if args, ok := isFmtSprintfCall(pass, msgAndArgs); ok { + if checker.requireFFuncs { + msg := fmt.Sprintf("remove unnecessary fmt.Sprintf and use %s.%s", call.SelectorXStr, fFunc) + return newDiagnostic(checker.Name(), call, msg, + newSuggestedFuncReplacement(call, fFunc, analysis.TextEdit{ + Pos: msgAndArgs.Pos(), + End: msgAndArgs.End(), + NewText: formatAsCallArgs(pass, args...), + }), + ) + } + return newRemoveSprintfDiagnostic(pass, checker.Name(), call, msgAndArgs, args) + } + } + + if checker.requireFFuncs { + return newUseFunctionDiagnostic(checker.Name(), call, fFunc, newSuggestedFuncReplacement(call, fFunc)) + } + return nil +} + +func (checker Formatter) checkFmtAssertion(pass *analysis.Pass, call *CallMeta) (result *analysis.Diagnostic) { + formatPos := getMsgPosition(call.Fn.Signature) + if formatPos < 0 { + return nil + } + + msg := call.ArgsRaw[formatPos] + + if formatPos == len(call.ArgsRaw)-1 { + if args, ok := isFmtSprintfCall(pass, msg); ok { + return newRemoveSprintfDiagnostic(pass, checker.Name(), call, msg, args) + } + } + + if checker.checkFormatString { + report := pass.Report + defer func() { pass.Report = report }() + + pass.Report = func(d analysis.Diagnostic) { + result = newDiagnostic(checker.Name(), call, d.Message, nil) + } + + format, err := strconv.Unquote(analysisutil.NodeString(pass.Fset, msg)) + if err != nil { + return nil + } + printf.CheckPrintf(pass, call.Call, call.String(), format, formatPos) + } + return result +} + +func isPrintfLikeCall(pass *analysis.Pass, call *CallMeta, sig *types.Signature) (int, bool) { + msgAndArgsPos := getMsgAndArgsPosition(sig) + if msgAndArgsPos < 0 { + return -1, false + } + + fmtFn := analysisutil.ObjectOf(pass.Pkg, testify.AssertPkgPath, call.Fn.Name+"f") + if fmtFn == nil { + // NOTE(a.telyshev): No formatted analogue of assertion. + return -1, false + } + + return msgAndArgsPos, len(call.ArgsRaw) > msgAndArgsPos +} + +func getMsgAndArgsPosition(sig *types.Signature) int { + params := sig.Params() + if params.Len() < 1 { + return -1 + } + + lastIdx := params.Len() - 1 + lastParam := params.At(lastIdx) + + _, isSlice := lastParam.Type().(*types.Slice) + if lastParam.Name() == "msgAndArgs" && isSlice { + return lastIdx + } + return -1 +} + +func getMsgPosition(sig *types.Signature) int { + for i := 0; i < sig.Params().Len(); i++ { + param := sig.Params().At(i) + + if b, ok := param.Type().(*types.Basic); ok && b.Kind() == types.String && param.Name() == "msg" { + return i + } + } + return -1 +} + +func isFmtSprintfCall(pass *analysis.Pass, expr ast.Expr) ([]ast.Expr, bool) { + ce, ok := expr.(*ast.CallExpr) + if !ok { + return nil, false + } + + se, ok := ce.Fun.(*ast.SelectorExpr) + if !ok { + return nil, false + } + + sprintfObj := analysisutil.ObjectOf(pass.Pkg, "fmt", "Sprintf") + if sprintfObj == nil { + return nil, false + } + + if !analysisutil.IsObj(pass.TypesInfo, se.Sel, sprintfObj) { + return nil, false + } + + return ce.Args, true +} diff --git a/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_basic_type.go b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_basic_type.go index 34dcb475f3..432a3032c9 100644 --- a/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_basic_type.go +++ b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_basic_type.go @@ -11,10 +11,51 @@ import ( func isZero(e ast.Expr) bool { return isIntNumber(e, 0) } -func isNotZero(e ast.Expr) bool { return !isZero(e) } - func isOne(e ast.Expr) bool { return isIntNumber(e, 1) } +func isAnyZero(e ast.Expr) bool { + return isIntNumber(e, 0) || isTypedSignedIntNumber(e, 0) || isTypedUnsignedIntNumber(e, 0) +} + +func isNotAnyZero(e ast.Expr) bool { + return !isAnyZero(e) +} + +func isZeroOrSignedZero(e ast.Expr) bool { + return isIntNumber(e, 0) || isTypedSignedIntNumber(e, 0) +} + +func isSignedNotZero(pass *analysis.Pass, e ast.Expr) bool { + return !isUnsigned(pass, e) && !isZeroOrSignedZero(e) +} + +func isTypedSignedIntNumber(e ast.Expr, v int) bool { + return isTypedIntNumber(e, v, "int", "int8", "int16", "int32", "int64") +} + +func isTypedUnsignedIntNumber(e ast.Expr, v int) bool { + return isTypedIntNumber(e, v, "uint", "uint8", "uint16", "uint32", "uint64") +} + +func isTypedIntNumber(e ast.Expr, v int, types ...string) bool { + ce, ok := e.(*ast.CallExpr) + if !ok || len(ce.Args) != 1 { + return false + } + + fn, ok := ce.Fun.(*ast.Ident) + if !ok { + return false + } + + for _, t := range types { + if fn.Name == t { + return isIntNumber(ce.Args[0], v) + } + } + return false +} + func isIntNumber(e ast.Expr, v int) bool { bl, ok := e.(*ast.BasicLit) return ok && bl.Kind == token.INT && bl.Value == fmt.Sprintf("%d", v) @@ -30,32 +71,43 @@ func isIntBasicLit(e ast.Expr) bool { return ok && bl.Kind == token.INT } -func isUntypedConst(p *analysis.Pass, e ast.Expr) bool { - t := p.TypesInfo.TypeOf(e) - if t == nil { - return false - } - - b, ok := t.(*types.Basic) - return ok && b.Info()&types.IsUntyped > 0 +func isUntypedConst(pass *analysis.Pass, e ast.Expr) bool { + return isUnderlying(pass, e, types.IsUntyped) } -func isTypedConst(p *analysis.Pass, e ast.Expr) bool { - tt, ok := p.TypesInfo.Types[e] +func isTypedConst(pass *analysis.Pass, e ast.Expr) bool { + tt, ok := pass.TypesInfo.Types[e] return ok && tt.IsValue() && tt.Value != nil } -func isFloat(pass *analysis.Pass, expr ast.Expr) bool { - t := pass.TypesInfo.TypeOf(expr) +func isFloat(pass *analysis.Pass, e ast.Expr) bool { + return isUnderlying(pass, e, types.IsFloat) +} + +func isUnsigned(pass *analysis.Pass, e ast.Expr) bool { + return isUnderlying(pass, e, types.IsUnsigned) +} + +func isUnderlying(pass *analysis.Pass, e ast.Expr, flag types.BasicInfo) bool { + t := pass.TypesInfo.TypeOf(e) if t == nil { return false } bt, ok := t.Underlying().(*types.Basic) - return ok && (bt.Info()&types.IsFloat > 0) + return ok && (bt.Info()&flag > 0) } -func isPointer(pass *analysis.Pass, expr ast.Expr) bool { - _, ok := pass.TypesInfo.TypeOf(expr).(*types.Pointer) +func isPointer(pass *analysis.Pass, e ast.Expr) bool { + _, ok := pass.TypesInfo.TypeOf(e).(*types.Pointer) return ok } + +// untype returns v from type(v) expression or v itself if there is no type cast. +func untype(e ast.Expr) ast.Expr { + ce, ok := e.(*ast.CallExpr) + if !ok || len(ce.Args) != 1 { + return e + } + return ce.Args[0] +} diff --git a/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_diagnostic.go b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_diagnostic.go index 4ab69c69bb..3ae88a560e 100644 --- a/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_diagnostic.go +++ b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_diagnostic.go @@ -2,6 +2,7 @@ package checkers import ( "fmt" + "go/ast" "golang.org/x/tools/go/analysis" ) @@ -21,6 +22,25 @@ func newUseFunctionDiagnostic( return newDiagnostic(checker, call, msg, fix) } +func newRemoveSprintfDiagnostic( + pass *analysis.Pass, + checker string, + call *CallMeta, + sprintfPos analysis.Range, + sprintfArgs []ast.Expr, +) *analysis.Diagnostic { + return newDiagnostic(checker, call, "remove unnecessary fmt.Sprintf", &analysis.SuggestedFix{ + Message: "Remove `fmt.Sprintf`", + TextEdits: []analysis.TextEdit{ + { + Pos: sprintfPos.Pos(), + End: sprintfPos.End(), + NewText: formatAsCallArgs(pass, sprintfArgs...), + }, + }, + }) +} + func newDiagnostic( checker string, rng analysis.Range, diff --git a/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_format.go b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_format.go index c8719551c2..765fce5276 100644 --- a/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_format.go +++ b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_format.go @@ -9,10 +9,18 @@ import ( "github.com/Antonboom/testifylint/internal/analysisutil" ) -// formatAsCallArgs joins a and b and return bytes like `a, b`. -func formatAsCallArgs(pass *analysis.Pass, a, b ast.Node) []byte { - return bytes.Join([][]byte{ - analysisutil.NodeBytes(pass.Fset, a), - analysisutil.NodeBytes(pass.Fset, b), - }, []byte(", ")) +// formatAsCallArgs joins a, b and c and returns bytes like `a, b, c`. +func formatAsCallArgs(pass *analysis.Pass, args ...ast.Expr) []byte { + if len(args) == 0 { + return []byte("") + } + + var buf bytes.Buffer + for i, arg := range args { + buf.Write(analysisutil.NodeBytes(pass.Fset, arg)) + if i != len(args)-1 { + buf.WriteString(", ") + } + } + return buf.Bytes() } diff --git a/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_interface.go b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_interface.go index 0b7f405762..b0c0d13020 100644 --- a/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_interface.go +++ b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_interface.go @@ -20,13 +20,13 @@ func isEmptyInterface(pass *analysis.Pass, expr ast.Expr) bool { return ok && iface.NumMethods() == 0 } -func implementsTestifySuite(pass *analysis.Pass, rcv ast.Expr) bool { +func implementsTestifySuite(pass *analysis.Pass, e ast.Expr) bool { suiteIfaceObj := analysisutil.ObjectOf(pass.Pkg, testify.SuitePkgPath, "TestingSuite") - return (suiteIfaceObj != nil) && implements(pass, rcv, suiteIfaceObj) + return (suiteIfaceObj != nil) && implements(pass, e, suiteIfaceObj) } -func implementsTestingT(pass *analysis.Pass, arg ast.Expr) bool { - return implementsAssertTestingT(pass, arg) || implementsRequireTestingT(pass, arg) +func implementsTestingT(pass *analysis.Pass, e ast.Expr) bool { + return implementsAssertTestingT(pass, e) || implementsRequireTestingT(pass, e) } func implementsAssertTestingT(pass *analysis.Pass, e ast.Expr) bool { diff --git a/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_naming.go b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_naming.go index e97c5117b8..1d92e3e810 100644 --- a/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_naming.go +++ b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_naming.go @@ -5,17 +5,22 @@ import ( "regexp" ) -func isStructVarNamedAsExpected(pattern *regexp.Regexp, e ast.Expr) bool { +func isStructVarNamedAfterPattern(pattern *regexp.Regexp, e ast.Expr) bool { s, ok := e.(*ast.SelectorExpr) - return ok && isIdentNamedAsExpected(pattern, s.X) + return ok && isIdentNamedAfterPattern(pattern, s.X) } -func isStructFieldNamedAsExpected(pattern *regexp.Regexp, e ast.Expr) bool { +func isStructFieldNamedAfterPattern(pattern *regexp.Regexp, e ast.Expr) bool { s, ok := e.(*ast.SelectorExpr) - return ok && isIdentNamedAsExpected(pattern, s.Sel) + return ok && isIdentNamedAfterPattern(pattern, s.Sel) } -func isIdentNamedAsExpected(pattern *regexp.Regexp, e ast.Expr) bool { +func isIdentNamedAfterPattern(pattern *regexp.Regexp, e ast.Expr) bool { id, ok := e.(*ast.Ident) return ok && pattern.MatchString(id.Name) } + +func isIdentWithName(name string, e ast.Expr) bool { + id, ok := e.(*ast.Ident) + return ok && id.Name == name +} diff --git a/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_nil.go b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_nil.go index 2707adb465..112fca38e7 100644 --- a/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_nil.go +++ b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/helpers_nil.go @@ -14,6 +14,5 @@ func xorNil(first, second ast.Expr) (ast.Expr, bool) { } func isNil(expr ast.Expr) bool { - ident, ok := expr.(*ast.Ident) - return ok && ident.Name == "nil" + return isIdentWithName("nil", expr) } diff --git a/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/negative_postive.go b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/negative_postive.go index c3f7175c2f..274021f679 100644 --- a/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/negative_postive.go +++ b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/negative_postive.go @@ -29,6 +29,8 @@ import ( // // assert.Negative(t, value) // assert.Positive(t, value) +// +// Typed zeros (like `int8(0)`, ..., `uint64(0)`) are also supported. type NegativePositive struct{} // NewNegativePositive constructs NegativePositive checker. @@ -54,6 +56,8 @@ func (checker NegativePositive) checkNegative(pass *analysis.Pass, call *CallMet ) } + // NOTE(a.telyshev): We ignore uint-asserts as being no sense for assert.Negative. + switch call.Fn.NameFTrimmed { case "Less": if len(call.Args) < 2 { @@ -61,8 +65,8 @@ func (checker NegativePositive) checkNegative(pass *analysis.Pass, call *CallMet } a, b := call.Args[0], call.Args[1] - if isNotZero(a) && isZero(b) { - return newUseNegativeDiagnostic(a.Pos(), b.End(), a) + if isSignedNotZero(pass, a) && isZeroOrSignedZero(b) { + return newUseNegativeDiagnostic(a.Pos(), b.End(), untype(a)) } case "Greater": @@ -71,8 +75,8 @@ func (checker NegativePositive) checkNegative(pass *analysis.Pass, call *CallMet } a, b := call.Args[0], call.Args[1] - if isZero(a) && isNotZero(b) { - return newUseNegativeDiagnostic(a.Pos(), b.End(), b) + if isZeroOrSignedZero(a) && isSignedNotZero(pass, b) { + return newUseNegativeDiagnostic(a.Pos(), b.End(), untype(b)) } case "True": @@ -81,12 +85,12 @@ func (checker NegativePositive) checkNegative(pass *analysis.Pass, call *CallMet } expr := call.Args[0] - a, _, ok1 := isStrictComparisonWith(pass, expr, p(isNotZero), token.LSS, p(isZero)) // a < 0 - _, b, ok2 := isStrictComparisonWith(pass, expr, p(isZero), token.GTR, p(isNotZero)) // 0 > a + a, _, ok1 := isStrictComparisonWith(pass, expr, isSignedNotZero, token.LSS, p(isZeroOrSignedZero)) // a < 0 + _, b, ok2 := isStrictComparisonWith(pass, expr, p(isZeroOrSignedZero), token.GTR, isSignedNotZero) // 0 > a survivingArg, ok := anyVal([]bool{ok1, ok2}, a, b) if ok { - return newUseNegativeDiagnostic(expr.Pos(), expr.End(), survivingArg) + return newUseNegativeDiagnostic(expr.Pos(), expr.End(), untype(survivingArg)) } case "False": @@ -95,12 +99,12 @@ func (checker NegativePositive) checkNegative(pass *analysis.Pass, call *CallMet } expr := call.Args[0] - a, _, ok1 := isStrictComparisonWith(pass, expr, p(isNotZero), token.GEQ, p(isZero)) // a >= 0 - _, b, ok2 := isStrictComparisonWith(pass, expr, p(isZero), token.LEQ, p(isNotZero)) // 0 <= a + a, _, ok1 := isStrictComparisonWith(pass, expr, isSignedNotZero, token.GEQ, p(isZeroOrSignedZero)) // a >= 0 + _, b, ok2 := isStrictComparisonWith(pass, expr, p(isZeroOrSignedZero), token.LEQ, isSignedNotZero) // 0 <= a survivingArg, ok := anyVal([]bool{ok1, ok2}, a, b) if ok { - return newUseNegativeDiagnostic(expr.Pos(), expr.End(), survivingArg) + return newUseNegativeDiagnostic(expr.Pos(), expr.End(), untype(survivingArg)) } } return nil @@ -125,8 +129,8 @@ func (checker NegativePositive) checkPositive(pass *analysis.Pass, call *CallMet } a, b := call.Args[0], call.Args[1] - if isNotZero(a) && isZero(b) { - return newUsePositiveDiagnostic(a.Pos(), b.End(), a) + if isNotAnyZero(a) && isAnyZero(b) { + return newUsePositiveDiagnostic(a.Pos(), b.End(), untype(a)) } case "Less": @@ -135,8 +139,8 @@ func (checker NegativePositive) checkPositive(pass *analysis.Pass, call *CallMet } a, b := call.Args[0], call.Args[1] - if isZero(a) && isNotZero(b) { - return newUsePositiveDiagnostic(a.Pos(), b.End(), b) + if isAnyZero(a) && isNotAnyZero(b) { + return newUsePositiveDiagnostic(a.Pos(), b.End(), untype(b)) } case "True": @@ -145,12 +149,12 @@ func (checker NegativePositive) checkPositive(pass *analysis.Pass, call *CallMet } expr := call.Args[0] - a, _, ok1 := isStrictComparisonWith(pass, expr, p(isNotZero), token.GTR, p(isZero)) // a > 0 - _, b, ok2 := isStrictComparisonWith(pass, expr, p(isZero), token.LSS, p(isNotZero)) // 0 < a + a, _, ok1 := isStrictComparisonWith(pass, expr, p(isNotAnyZero), token.GTR, p(isAnyZero)) // a > 0 + _, b, ok2 := isStrictComparisonWith(pass, expr, p(isAnyZero), token.LSS, p(isNotAnyZero)) // 0 < a survivingArg, ok := anyVal([]bool{ok1, ok2}, a, b) if ok { - return newUsePositiveDiagnostic(expr.Pos(), expr.End(), survivingArg) + return newUsePositiveDiagnostic(expr.Pos(), expr.End(), untype(survivingArg)) } case "False": @@ -159,12 +163,12 @@ func (checker NegativePositive) checkPositive(pass *analysis.Pass, call *CallMet } expr := call.Args[0] - a, _, ok1 := isStrictComparisonWith(pass, expr, p(isNotZero), token.LEQ, p(isZero)) // a <= 0 - _, b, ok2 := isStrictComparisonWith(pass, expr, p(isZero), token.GEQ, p(isNotZero)) // 0 >= a + a, _, ok1 := isStrictComparisonWith(pass, expr, p(isNotAnyZero), token.LEQ, p(isAnyZero)) // a <= 0 + _, b, ok2 := isStrictComparisonWith(pass, expr, p(isAnyZero), token.GEQ, p(isNotAnyZero)) // 0 >= a survivingArg, ok := anyVal([]bool{ok1, ok2}, a, b) if ok { - return newUsePositiveDiagnostic(expr.Pos(), expr.End(), survivingArg) + return newUsePositiveDiagnostic(expr.Pos(), expr.End(), untype(survivingArg)) } } return nil diff --git a/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/printf/LICENSE b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/printf/LICENSE new file mode 100644 index 0000000000..6a66aea5ea --- /dev/null +++ b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/printf/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/printf/doc.go b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/printf/doc.go new file mode 100644 index 0000000000..09cd239937 --- /dev/null +++ b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/printf/doc.go @@ -0,0 +1,6 @@ +// Package printf is a patched fork of +// https://github.com/golang/tools/blob/b6235391adb3b7f8bcfc4df81055e8f023de2688/go/analysis/passes/printf/printf.go#L538 +// +// Initial discussion: +// https://go-review.googlesource.com/c/tools/+/580555/comments/dfe3ef96_b1b815d5 +package printf diff --git a/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/printf/printf.go b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/printf/printf.go new file mode 100644 index 0000000000..cfb47b5426 --- /dev/null +++ b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/printf/printf.go @@ -0,0 +1,559 @@ +// Copyright 2010 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 printf + +import ( + "bytes" + "fmt" + "go/ast" + "go/token" + "go/types" + "strconv" + "strings" + "unicode/utf8" + + "golang.org/x/tools/go/analysis" + + "github.com/Antonboom/testifylint/internal/analysisutil" +) + +// CheckPrintf checks a call to a formatted print routine such as Printf. +func CheckPrintf( + pass *analysis.Pass, + call *ast.CallExpr, + fnName string, + format string, + formatIdx int, +) { + firstArg := formatIdx + 1 // Arguments are immediately after format string. + if !strings.Contains(format, "%") { + if len(call.Args) > firstArg { + pass.Reportf(call.Lparen, "%s call has arguments but no formatting directives", fnName) + } + return + } + // Hard part: check formats against args. + argNum := firstArg + maxArgNum := firstArg + anyIndex := false + for i, w := 0, 0; i < len(format); i += w { + w = 1 + if format[i] != '%' { + continue + } + state := parsePrintfVerb(pass, call, fnName, format[i:], firstArg, argNum) + if state == nil { + return + } + w = len(state.format) + if !okPrintfArg(pass, call, state) { // One error per format is enough. + return + } + if state.hasIndex { + anyIndex = true + } + if state.verb == 'w' { + pass.Reportf(call.Pos(), "%s does not support error-wrapping directive %%w", state.name) + return + } + if len(state.argNums) > 0 { + // Continue with the next sequential argument. + argNum = state.argNums[len(state.argNums)-1] + 1 + } + for _, n := range state.argNums { + if n >= maxArgNum { + maxArgNum = n + 1 + } + } + } + // Dotdotdot is hard. + if call.Ellipsis.IsValid() && maxArgNum >= len(call.Args)-1 { + return + } + // If any formats are indexed, extra arguments are ignored. + if anyIndex { + return + } + // There should be no leftover arguments. + if maxArgNum != len(call.Args) { + expect := maxArgNum - firstArg + numArgs := len(call.Args) - firstArg + pass.ReportRangef(call, "%s call needs %v but has %v", fnName, count(expect, "arg"), count(numArgs, "arg")) + } +} + +// formatState holds the parsed representation of a printf directive such as "%3.*[4]d". +// It is constructed by parsePrintfVerb. +type formatState struct { + verb rune // the format verb: 'd' for "%d" + format string // the full format directive from % through verb, "%.3d". + name string // Printf, Sprintf etc. + flags []byte // the list of # + etc. + argNums []int // the successive argument numbers that are consumed, adjusted to refer to actual arg in call + firstArg int // Index of first argument after the format in the Printf call. + // Used only during parse. + pass *analysis.Pass + call *ast.CallExpr + argNum int // Which argument we're expecting to format now. + hasIndex bool // Whether the argument is indexed. + indexPending bool // Whether we have an indexed argument that has not resolved. + nbytes int // number of bytes of the format string consumed. +} + +// parseFlags accepts any printf flags. +func (s *formatState) parseFlags() { + for s.nbytes < len(s.format) { + switch c := s.format[s.nbytes]; c { + case '#', '0', '+', '-', ' ': + s.flags = append(s.flags, c) + s.nbytes++ + default: + return + } + } +} + +// scanNum advances through a decimal number if present. +func (s *formatState) scanNum() { + for ; s.nbytes < len(s.format); s.nbytes++ { + c := s.format[s.nbytes] + if c < '0' || '9' < c { + return + } + } +} + +// parseIndex scans an index expression. It returns false if there is a syntax error. +func (s *formatState) parseIndex() bool { + if s.nbytes == len(s.format) || s.format[s.nbytes] != '[' { + return true + } + // Argument index present. + s.nbytes++ // skip '[' + start := s.nbytes + s.scanNum() + ok := true + if s.nbytes == len(s.format) || s.nbytes == start || s.format[s.nbytes] != ']' { + ok = false // syntax error is either missing "]" or invalid index. + s.nbytes = strings.Index(s.format[start:], "]") + if s.nbytes < 0 { + s.pass.ReportRangef(s.call, "%s format %s is missing closing ]", s.name, s.format) + return false + } + s.nbytes += start + } + arg32, err := strconv.ParseInt(s.format[start:s.nbytes], 10, 32) + if err != nil || !ok || arg32 <= 0 || arg32 > int64(len(s.call.Args)-s.firstArg) { + s.pass.ReportRangef(s.call, "%s format has invalid argument index [%s]", s.name, s.format[start:s.nbytes]) + return false + } + s.nbytes++ // skip ']' + arg := int(arg32) + arg += s.firstArg - 1 // We want to zero-index the actual arguments. + s.argNum = arg + s.hasIndex = true + s.indexPending = true + return true +} + +// parseNum scans a width or precision (or *). It returns false if there's a bad index expression. +func (s *formatState) parseNum() bool { + if s.nbytes < len(s.format) && s.format[s.nbytes] == '*' { + if s.indexPending { // Absorb it. + s.indexPending = false + } + s.nbytes++ + s.argNums = append(s.argNums, s.argNum) + s.argNum++ + } else { + s.scanNum() + } + return true +} + +// parsePrecision scans for a precision. It returns false if there's a bad index expression. +func (s *formatState) parsePrecision() bool { + // If there's a period, there may be a precision. + if s.nbytes < len(s.format) && s.format[s.nbytes] == '.' { + s.flags = append(s.flags, '.') // Treat precision as a flag. + s.nbytes++ + if !s.parseIndex() { + return false + } + if !s.parseNum() { + return false + } + } + return true +} + +// isFormatter reports whether t could satisfy fmt.Formatter. +// The only interface method to look for is "Format(State, rune)". +func isFormatter(typ types.Type) bool { + // If the type is an interface, the value it holds might satisfy fmt.Formatter. + if _, ok := typ.Underlying().(*types.Interface); ok { + // Don't assume type parameters could be formatters. With the greater + // expressiveness of constraint interface syntax we expect more type safety + // when using type parameters. + if !isTypeParam(typ) { + return true + } + } + obj, _, _ := types.LookupFieldOrMethod(typ, false, nil, "Format") + fn, ok := obj.(*types.Func) + if !ok { + return false + } + sig := fn.Type().(*types.Signature) + return sig.Params().Len() == 2 && + sig.Results().Len() == 0 && + isNamedType(sig.Params().At(0).Type(), "fmt", "State") && + types.Identical(sig.Params().At(1).Type(), types.Typ[types.Rune]) +} + +// isTypeParam reports whether t is a type parameter. +func isTypeParam(t types.Type) bool { + _, ok := t.(*types.TypeParam) + return ok +} + +// isNamedType reports whether t is the named type with the given package path +// and one of the given names. +// This function avoids allocating the concatenation of "pkg.Name", +// which is important for the performance of syntax matching. +func isNamedType(t types.Type, pkgPath string, names ...string) bool { + n, ok := t.(*types.Named) + if !ok { + return false + } + obj := n.Obj() + if obj == nil || obj.Pkg() == nil || obj.Pkg().Path() != pkgPath { + return false + } + name := obj.Name() + for _, n := range names { + if name == n { + return true + } + } + return false +} + +// parsePrintfVerb looks the formatting directive that begins the format string +// and returns a formatState that encodes what the directive wants, without looking +// at the actual arguments present in the call. The result is nil if there is an error. +func parsePrintfVerb(pass *analysis.Pass, call *ast.CallExpr, name, format string, firstArg, argNum int) *formatState { + state := &formatState{ + format: format, + name: name, + flags: make([]byte, 0, 5), + argNum: argNum, + argNums: make([]int, 0, 1), + nbytes: 1, // There's guaranteed to be a percent sign. + firstArg: firstArg, + pass: pass, + call: call, + } + // There may be flags. + state.parseFlags() + // There may be an index. + if !state.parseIndex() { + return nil + } + // There may be a width. + if !state.parseNum() { + return nil + } + // There may be a precision. + if !state.parsePrecision() { + return nil + } + // Now a verb, possibly prefixed by an index (which we may already have). + if !state.indexPending && !state.parseIndex() { + return nil + } + if state.nbytes == len(state.format) { + pass.ReportRangef(call.Fun, "%s format %s is missing verb at end of string", name, state.format) + return nil + } + verb, w := utf8.DecodeRuneInString(state.format[state.nbytes:]) + state.verb = verb + state.nbytes += w + if verb != '%' { + state.argNums = append(state.argNums, state.argNum) + } + state.format = state.format[:state.nbytes] + return state +} + +// printfArgType encodes the types of expressions a printf verb accepts. It is a bitmask. +type printfArgType int + +const ( + argBool printfArgType = 1 << iota + argInt + argRune + argString + argFloat + argComplex + argPointer + argError + anyType printfArgType = ^0 +) + +type printVerb struct { + verb rune // User may provide verb through Formatter; could be a rune. + flags string // known flags are all ASCII + typ printfArgType +} + +// Common flag sets for printf verbs. +const ( + noFlag = "" + numFlag = " -+.0" + sharpNumFlag = " -+.0#" + allFlags = " -+.0#" +) + +// printVerbs identifies which flags are known to printf for each verb. +var printVerbs = []printVerb{ + // '-' is a width modifier, always valid. + // '.' is a precision for float, max width for strings. + // '+' is required sign for numbers, Go format for %v. + // '#' is alternate format for several verbs. + // ' ' is spacer for numbers + {'%', noFlag, 0}, + {'b', sharpNumFlag, argInt | argFloat | argComplex | argPointer}, + {'c', "-", argRune | argInt}, + {'d', numFlag, argInt | argPointer}, + {'e', sharpNumFlag, argFloat | argComplex}, + {'E', sharpNumFlag, argFloat | argComplex}, + {'f', sharpNumFlag, argFloat | argComplex}, + {'F', sharpNumFlag, argFloat | argComplex}, + {'g', sharpNumFlag, argFloat | argComplex}, + {'G', sharpNumFlag, argFloat | argComplex}, + {'o', sharpNumFlag, argInt | argPointer}, + {'O', sharpNumFlag, argInt | argPointer}, + {'p', "-#", argPointer}, + {'q', " -+.0#", argRune | argInt | argString}, + {'s', " -+.0", argString}, + {'t', "-", argBool}, + {'T', "-", anyType}, + {'U', "-#", argRune | argInt}, + {'v', allFlags, anyType}, + {'w', allFlags, argError}, + {'x', sharpNumFlag, argRune | argInt | argString | argPointer | argFloat | argComplex}, + {'X', sharpNumFlag, argRune | argInt | argString | argPointer | argFloat | argComplex}, +} + +// okPrintfArg compares the formatState to the arguments actually present, +// reporting any discrepancies it can discern. If the final argument is ellipsissed, +// there's little it can do for that. +func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, state *formatState) (ok bool) { + var v printVerb + found := false + // Linear scan is fast enough for a small list. + for _, v = range printVerbs { + if v.verb == state.verb { + found = true + break + } + } + + // Could current arg implement fmt.Formatter? + // Skip check for the %w verb, which requires an error. + formatter := false + if v.typ != argError && state.argNum < len(call.Args) { + if tv, ok := pass.TypesInfo.Types[call.Args[state.argNum]]; ok { + formatter = isFormatter(tv.Type) + } + } + + if !formatter { + if !found { + pass.ReportRangef(call, "%s format %s has unknown verb %c", state.name, state.format, state.verb) + return false + } + for _, flag := range state.flags { + // TODO: Disable complaint about '0' for Go 1.10. To be fixed properly in 1.11. + // See issues 23598 and 23605. + if flag == '0' { + continue + } + if !strings.ContainsRune(v.flags, rune(flag)) { + pass.ReportRangef(call, "%s format %s has unrecognized flag %c", state.name, state.format, flag) + return false + } + } + } + // Verb is good. If len(state.argNums)>trueArgs, we have something like %.*s and all + // but the final arg must be an integer. + trueArgs := 1 + if state.verb == '%' { + trueArgs = 0 + } + nargs := len(state.argNums) + for i := 0; i < nargs-trueArgs; i++ { + if !argCanBeChecked(pass, call, i, state) { + return + } + // NOTE(a.telyshev): `matchArgType` leads to a lot of "golang.org/x/tools/internal" code. + /* + argNum := state.argNums[i] + arg := call.Args[argNum] + + if reason, ok := matchArgType(pass, argInt, arg); !ok { + details := "" + if reason != "" { + details = " (" + reason + ")" + } + pass.ReportRangef(call, "%s format %s uses non-int %s%s as argument of *", state.name, state.format, analysisutil.Format(pass.Fset, arg), details) + return false + } + */ + } + + if state.verb == '%' || formatter { + return true + } + argNum := state.argNums[len(state.argNums)-1] + if !argCanBeChecked(pass, call, len(state.argNums)-1, state) { + return false + } + arg := call.Args[argNum] + if isFunctionValue(pass, arg) && state.verb != 'p' && state.verb != 'T' { + pass.ReportRangef(call, "%s format %s arg %s is a func value, not called", state.name, state.format, analysisutil.NodeString(pass.Fset, arg)) + return false + } + // NOTE(a.telyshev): `matchArgType` leads to a lot of "golang.org/x/tools/internal" code. + /* + if reason, ok := matchArgType(pass, v.typ, arg); !ok { + typeString := "" + if typ := pass.TypesInfo.Types[arg].Type; typ != nil { + typeString = typ.String() + } + details := "" + if reason != "" { + details = " (" + reason + ")" + } + pass.ReportRangef(call, "%s format %s has arg %s of wrong type %s%s", state.name, state.format, analysisutil.Format(pass.Fset, arg), typeString, details) + return false + } + */ + if v.typ&argString != 0 && v.verb != 'T' && !bytes.Contains(state.flags, []byte{'#'}) { + if methodName, ok := recursiveStringer(pass, arg); ok { + pass.ReportRangef(call, "%s format %s with arg %s causes recursive %s method call", state.name, state.format, analysisutil.NodeString(pass.Fset, arg), methodName) + return false + } + } + return true +} + +// recursiveStringer reports whether the argument e is a potential +// recursive call to stringer or is an error, such as t and &t in these examples: +// +// func (t *T) String() string { printf("%s", t) } +// func (t T) Error() string { printf("%s", t) } +// func (t T) String() string { printf("%s", &t) } +func recursiveStringer(pass *analysis.Pass, e ast.Expr) (string, bool) { + typ := pass.TypesInfo.Types[e].Type + + // It's unlikely to be a recursive stringer if it has a Format method. + if isFormatter(typ) { + return "", false + } + + // Does e allow e.String() or e.Error()? + strObj, _, _ := types.LookupFieldOrMethod(typ, false, pass.Pkg, "String") + strMethod, strOk := strObj.(*types.Func) + errObj, _, _ := types.LookupFieldOrMethod(typ, false, pass.Pkg, "Error") + errMethod, errOk := errObj.(*types.Func) + if !strOk && !errOk { + return "", false + } + + // inScope returns true if e is in the scope of f. + inScope := func(e ast.Expr, f *types.Func) bool { + return f.Scope() != nil && f.Scope().Contains(e.Pos()) + } + + // Is the expression e within the body of that String or Error method? + var method *types.Func + if strOk && strMethod.Pkg() == pass.Pkg && inScope(e, strMethod) { + method = strMethod + } else if errOk && errMethod.Pkg() == pass.Pkg && inScope(e, errMethod) { + method = errMethod + } else { + return "", false + } + + sig := method.Type().(*types.Signature) + if !isStringer(sig) { + return "", false + } + + // Is it the receiver r, or &r? + if u, ok := e.(*ast.UnaryExpr); ok && u.Op == token.AND { + e = u.X // strip off & from &r + } + if id, ok := e.(*ast.Ident); ok { + if pass.TypesInfo.Uses[id] == sig.Recv() { + return method.FullName(), true + } + } + return "", false +} + +// isStringer reports whether the method signature matches the String() definition in fmt.Stringer. +func isStringer(sig *types.Signature) bool { + return sig.Params().Len() == 0 && + sig.Results().Len() == 1 && + sig.Results().At(0).Type() == types.Typ[types.String] +} + +// isFunctionValue reports whether the expression is a function as opposed to a function call. +// It is almost always a mistake to print a function value. +func isFunctionValue(pass *analysis.Pass, e ast.Expr) bool { + if typ := pass.TypesInfo.Types[e].Type; typ != nil { + // Don't call Underlying: a named func type with a String method is ok. + // TODO(adonovan): it would be more precise to check isStringer. + _, ok := typ.(*types.Signature) + return ok + } + return false +} + +// argCanBeChecked reports whether the specified argument is statically present; +// it may be beyond the list of arguments or in a terminal slice... argument, which +// means we can't see it. +func argCanBeChecked(pass *analysis.Pass, call *ast.CallExpr, formatArg int, state *formatState) bool { + argNum := state.argNums[formatArg] + if argNum <= 0 { + return false + } + if argNum < len(call.Args)-1 { + return true // Always OK. + } + if call.Ellipsis.IsValid() { + return false // We just can't tell; there could be many more arguments. + } + if argNum < len(call.Args) { + return true + } + // There are bad indexes in the format or there are fewer arguments than the format needs. + // This is the argument number relative to the format: Printf("%s", "hi") will give 1 for the "hi". + arg := argNum - state.firstArg + 1 // People think of arguments as 1-indexed. + pass.ReportRangef(call, "%s format %s reads arg #%d, but call has %v", state.name, state.format, arg, count(len(call.Args)-state.firstArg, "arg")) + return false +} + +// count(n, what) returns "1 what" or "N whats" +// (assuming the plural of what is whats). +func count(n int, what string) string { + if n == 1 { + return "1 " + what + } + return fmt.Sprintf("%d %ss", n, what) +} diff --git a/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/suite_broken_parallel.go b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/suite_broken_parallel.go new file mode 100644 index 0000000000..f830fd2a58 --- /dev/null +++ b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/suite_broken_parallel.go @@ -0,0 +1,89 @@ +package checkers + +import ( + "fmt" + "go/ast" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/ast/inspector" + + "github.com/Antonboom/testifylint/internal/analysisutil" +) + +// SuiteBrokenParallel detects unsupported t.Parallel() call in suite tests +// +// func (s *MySuite) SetupTest() { +// s.T().Parallel() +// } +// +// // And other hooks... +// +// func (s *MySuite) TestSomething() { +// s.T().Parallel() +// +// for _, tt := range cases { +// s.Run(tt.name, func() { +// s.T().Parallel() +// }) +// +// s.T().Run(tt.name, func(t *testing.T) { +// t.Parallel() +// }) +// } +// } +type SuiteBrokenParallel struct{} + +// NewSuiteBrokenParallel constructs SuiteBrokenParallel checker. +func NewSuiteBrokenParallel() SuiteBrokenParallel { return SuiteBrokenParallel{} } +func (SuiteBrokenParallel) Name() string { return "suite-broken-parallel" } + +func (checker SuiteBrokenParallel) Check(pass *analysis.Pass, insp *inspector.Inspector) (diagnostics []analysis.Diagnostic) { + const report = "testify v1 does not support suite's parallel tests and subtests" + + insp.WithStack([]ast.Node{(*ast.CallExpr)(nil)}, func(node ast.Node, push bool, stack []ast.Node) bool { + if !push { + return false + } + ce := node.(*ast.CallExpr) + + se, ok := ce.Fun.(*ast.SelectorExpr) + if !ok { + return true + } + if !isIdentWithName("Parallel", se.Sel) { + return true + } + if !implementsTestingT(pass, se.X) { + return true + } + + for i := len(stack) - 2; i >= 0; i-- { + fd, ok := stack[i].(*ast.FuncDecl) + if !ok { + continue + } + + if !isSuiteMethod(pass, fd) { + continue + } + + nextLine := pass.Fset.Position(ce.Pos()).Line + 1 + d := newDiagnostic(checker.Name(), ce, report, &analysis.SuggestedFix{ + Message: fmt.Sprintf("Remove `%s` call", analysisutil.NodeString(pass.Fset, ce)), + TextEdits: []analysis.TextEdit{ + { + Pos: ce.Pos(), + End: pass.Fset.File(ce.Pos()).LineStart(nextLine), + NewText: []byte(""), + }, + }, + }) + + diagnostics = append(diagnostics, *d) + return false + } + + return true + }) + return diagnostics +} diff --git a/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/suite_dont_use_pkg.go b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/suite_dont_use_pkg.go index 7f3e9c7c81..6150ae78da 100644 --- a/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/suite_dont_use_pkg.go +++ b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/suite_dont_use_pkg.go @@ -7,7 +7,7 @@ import ( "golang.org/x/tools/go/analysis" ) -// SuiteDontUsePkg detects situation like +// SuiteDontUsePkg detects situations like // // func (s *MySuite) TestSomething() { // assert.Equal(s.T(), 42, value) diff --git a/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/suite_extra_assert_call.go b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/suite_extra_assert_call.go index c16f1ea63c..9adfe5190c 100644 --- a/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/suite_extra_assert_call.go +++ b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/suite_extra_assert_call.go @@ -19,7 +19,7 @@ const ( const DefaultSuiteExtraAssertCallMode = SuiteExtraAssertCallModeRemove -// SuiteExtraAssertCall detects situation like +// SuiteExtraAssertCall detects situations like // // func (s *MySuite) TestSomething() { // s.Assert().Equal(42, value) diff --git a/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/suite_subtest_run.go b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/suite_subtest_run.go new file mode 100644 index 0000000000..67d9c252b8 --- /dev/null +++ b/tools/vendor/github.com/Antonboom/testifylint/internal/checkers/suite_subtest_run.go @@ -0,0 +1,60 @@ +package checkers + +import ( + "fmt" + "go/ast" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/ast/inspector" + + "github.com/Antonboom/testifylint/internal/analysisutil" +) + +// SuiteSubtestRun detects situations like +// +// s.T().Run("subtest", func(t *testing.T) { +// assert.Equal(t, 42, result) +// }) +// +// and requires +// +// s.Run("subtest", func() { +// s.Equal(42, result) +// }) +type SuiteSubtestRun struct{} + +// NewSuiteSubtestRun constructs SuiteSubtestRun checker. +func NewSuiteSubtestRun() SuiteSubtestRun { return SuiteSubtestRun{} } +func (SuiteSubtestRun) Name() string { return "suite-subtest-run" } + +func (checker SuiteSubtestRun) Check(pass *analysis.Pass, insp *inspector.Inspector) (diagnostics []analysis.Diagnostic) { + insp.Preorder([]ast.Node{(*ast.CallExpr)(nil)}, func(node ast.Node) { + ce := node.(*ast.CallExpr) // s.T().Run + + se, ok := ce.Fun.(*ast.SelectorExpr) // s.T() + .Run + if !ok { + return + } + if !isIdentWithName("Run", se.Sel) { + return + } + + tCall, ok := se.X.(*ast.CallExpr) // s.T() + if !ok { + return + } + tCallSel, ok := tCall.Fun.(*ast.SelectorExpr) // s + .T() + if !ok { + return + } + if !isIdentWithName("T", tCallSel.Sel) { + return + } + + if implementsTestifySuite(pass, tCallSel.X) && implementsTestingT(pass, tCall) { + msg := fmt.Sprintf("use %s.Run to run subtest", analysisutil.NodeString(pass.Fset, tCallSel.X)) + diagnostics = append(diagnostics, *newDiagnostic(checker.Name(), ce, msg, nil)) + } + }) + return diagnostics +} diff --git a/tools/vendor/github.com/Antonboom/testifylint/internal/config/config.go b/tools/vendor/github.com/Antonboom/testifylint/internal/config/config.go index dd2eb9598e..a8812e6d01 100644 --- a/tools/vendor/github.com/Antonboom/testifylint/internal/config/config.go +++ b/tools/vendor/github.com/Antonboom/testifylint/internal/config/config.go @@ -21,6 +21,10 @@ func NewDefault() Config { ExpectedActual: ExpectedActualConfig{ ExpVarPattern: RegexpValue{checkers.DefaultExpectedVarPattern}, }, + Formatter: FormatterConfig{ + CheckFormatString: true, + RequireFFuncs: false, + }, GoRequire: GoRequireConfig{ IgnoreHTTPHandlers: false, }, @@ -42,6 +46,7 @@ type Config struct { BoolCompare BoolCompareConfig ExpectedActual ExpectedActualConfig + Formatter FormatterConfig GoRequire GoRequireConfig RequireError RequireErrorConfig SuiteExtraAssertCall SuiteExtraAssertCallConfig @@ -57,6 +62,12 @@ type ExpectedActualConfig struct { ExpVarPattern RegexpValue } +// FormatterConfig implements configuration of checkers.Formatter. +type FormatterConfig struct { + CheckFormatString bool + RequireFFuncs bool +} + // GoRequireConfig implements configuration of checkers.GoRequire. type GoRequireConfig struct { IgnoreHTTPHandlers bool @@ -109,14 +120,32 @@ func BindToFlags(cfg *Config, fs *flag.FlagSet) { fs.BoolVar(&cfg.DisableAll, "disable-all", false, "disable all checkers") fs.Var(&cfg.EnabledCheckers, "enable", "comma separated list of enabled checkers (in addition to enabled by default)") - fs.BoolVar(&cfg.BoolCompare.IgnoreCustomTypes, "bool-compare.ignore-custom-types", false, + fs.BoolVar(&cfg.BoolCompare.IgnoreCustomTypes, + "bool-compare.ignore-custom-types", false, "to ignore user defined types (over builtin bool)") - fs.Var(&cfg.ExpectedActual.ExpVarPattern, "expected-actual.pattern", "regexp for expected variable name") - fs.BoolVar(&cfg.GoRequire.IgnoreHTTPHandlers, "go-require.ignore-http-handlers", false, + + fs.Var(&cfg.ExpectedActual.ExpVarPattern, + "expected-actual.pattern", + "regexp for expected variable name") + + fs.BoolVar(&cfg.Formatter.CheckFormatString, + "formatter.check-format-string", true, + "to enable go vet's printf checks") + fs.BoolVar(&cfg.Formatter.RequireFFuncs, + "formatter.require-f-funcs", false, + "to require f-assertions if format string is used") + + fs.BoolVar(&cfg.GoRequire.IgnoreHTTPHandlers, + "go-require.ignore-http-handlers", false, "to ignore HTTP handlers (like http.HandlerFunc)") - fs.Var(&cfg.RequireError.FnPattern, "require-error.fn-pattern", "regexp for error assertions that should only be analyzed") + + fs.Var(&cfg.RequireError.FnPattern, + "require-error.fn-pattern", + "regexp for error assertions that should only be analyzed") + fs.Var(NewEnumValue(suiteExtraAssertCallModeAsString, &cfg.SuiteExtraAssertCall.Mode), - "suite-extra-assert-call.mode", "to require or remove extra Assert() call") + "suite-extra-assert-call.mode", + "to require or remove extra Assert() call") } var suiteExtraAssertCallModeAsString = map[string]checkers.SuiteExtraAssertCallMode{ diff --git a/tools/vendor/github.com/BurntSushi/toml/decode.go b/tools/vendor/github.com/BurntSushi/toml/decode.go index 7aaf462c94..c05a0b7e56 100644 --- a/tools/vendor/github.com/BurntSushi/toml/decode.go +++ b/tools/vendor/github.com/BurntSushi/toml/decode.go @@ -540,12 +540,14 @@ func (md *MetaData) badtype(dst string, data any) error { func (md *MetaData) parseErr(err error) error { k := md.context.String() + d := string(md.data) return ParseError{ + Message: err.Error(), + err: err, LastKey: k, - Position: md.keyInfo[k].pos, + Position: md.keyInfo[k].pos.withCol(d), Line: md.keyInfo[k].pos.Line, - err: err, - input: string(md.data), + input: d, } } diff --git a/tools/vendor/github.com/BurntSushi/toml/error.go b/tools/vendor/github.com/BurntSushi/toml/error.go index b45a3f45f6..1dd5232111 100644 --- a/tools/vendor/github.com/BurntSushi/toml/error.go +++ b/tools/vendor/github.com/BurntSushi/toml/error.go @@ -67,21 +67,36 @@ type ParseError struct { // Position of an error. type Position struct { Line int // Line number, starting at 1. + Col int // Error column, starting at 1. Start int // Start of error, as byte offset starting at 0. - Len int // Lenght in bytes. + Len int // Lenght of the error in bytes. } -func (pe ParseError) Error() string { - msg := pe.Message - if msg == "" { // Error from errorf() - msg = pe.err.Error() +func (p Position) withCol(tomlFile string) Position { + var ( + pos int + lines = strings.Split(tomlFile, "\n") + ) + for i := range lines { + ll := len(lines[i]) + 1 // +1 for the removed newline + if pos+ll >= p.Start { + p.Col = p.Start - pos + 1 + if p.Col < 1 { // Should never happen, but just in case. + p.Col = 1 + } + break + } + pos += ll } + return p +} +func (pe ParseError) Error() string { if pe.LastKey == "" { - return fmt.Sprintf("toml: line %d: %s", pe.Position.Line, msg) + return fmt.Sprintf("toml: line %d: %s", pe.Position.Line, pe.Message) } return fmt.Sprintf("toml: line %d (last key %q): %s", - pe.Position.Line, pe.LastKey, msg) + pe.Position.Line, pe.LastKey, pe.Message) } // ErrorWithPosition returns the error with detailed location context. @@ -92,26 +107,19 @@ func (pe ParseError) ErrorWithPosition() string { return pe.Error() } + // TODO: don't show control characters as literals? This may not show up + // well everywhere. + var ( lines = strings.Split(pe.input, "\n") - col = pe.column(lines) b = new(strings.Builder) ) - - msg := pe.Message - if msg == "" { - msg = pe.err.Error() - } - - // TODO: don't show control characters as literals? This may not show up - // well everywhere. - if pe.Position.Len == 1 { fmt.Fprintf(b, "toml: error: %s\n\nAt line %d, column %d:\n\n", - msg, pe.Position.Line, col+1) + pe.Message, pe.Position.Line, pe.Position.Col) } else { fmt.Fprintf(b, "toml: error: %s\n\nAt line %d, column %d-%d:\n\n", - msg, pe.Position.Line, col, col+pe.Position.Len) + pe.Message, pe.Position.Line, pe.Position.Col, pe.Position.Col+pe.Position.Len-1) } if pe.Position.Line > 2 { fmt.Fprintf(b, "% 7d | %s\n", pe.Position.Line-2, expandTab(lines[pe.Position.Line-3])) @@ -129,7 +137,7 @@ func (pe ParseError) ErrorWithPosition() string { diff := len(expanded) - len(lines[pe.Position.Line-1]) fmt.Fprintf(b, "% 7d | %s\n", pe.Position.Line, expanded) - fmt.Fprintf(b, "% 10s%s%s\n", "", strings.Repeat(" ", col+diff), strings.Repeat("^", pe.Position.Len)) + fmt.Fprintf(b, "% 10s%s%s\n", "", strings.Repeat(" ", pe.Position.Col-1+diff), strings.Repeat("^", pe.Position.Len)) return b.String() } @@ -151,23 +159,6 @@ func (pe ParseError) ErrorWithUsage() string { return m } -func (pe ParseError) column(lines []string) int { - var pos, col int - for i := range lines { - ll := len(lines[i]) + 1 // +1 for the removed newline - if pos+ll >= pe.Position.Start { - col = pe.Position.Start - pos - if col < 0 { // Should never happen, but just in case. - col = 0 - } - break - } - pos += ll - } - - return col -} - func expandTab(s string) string { var ( b strings.Builder diff --git a/tools/vendor/github.com/BurntSushi/toml/lex.go b/tools/vendor/github.com/BurntSushi/toml/lex.go index a1016d98a8..6878d9d698 100644 --- a/tools/vendor/github.com/BurntSushi/toml/lex.go +++ b/tools/vendor/github.com/BurntSushi/toml/lex.go @@ -492,6 +492,9 @@ func lexKeyEnd(lx *lexer) stateFn { lx.emit(itemKeyEnd) return lexSkip(lx, lexValue) default: + if r == '\n' { + return lx.errorPrevLine(fmt.Errorf("expected '.' or '=', but got %q instead", r)) + } return lx.errorf("expected '.' or '=', but got %q instead", r) } } @@ -560,6 +563,9 @@ func lexValue(lx *lexer) stateFn { if r == eof { return lx.errorf("unexpected EOF; expected value") } + if r == '\n' { + return lx.errorPrevLine(fmt.Errorf("expected value but found %q instead", r)) + } return lx.errorf("expected value but found %q instead", r) } diff --git a/tools/vendor/github.com/BurntSushi/toml/parse.go b/tools/vendor/github.com/BurntSushi/toml/parse.go index 11ac3108be..3f2c090c86 100644 --- a/tools/vendor/github.com/BurntSushi/toml/parse.go +++ b/tools/vendor/github.com/BurntSushi/toml/parse.go @@ -65,7 +65,7 @@ func parse(data string) (p *parser, err error) { if i := strings.IndexRune(data[:ex], 0); i > -1 { return nil, ParseError{ Message: "files cannot contain NULL bytes; probably using UTF-16; TOML files must be UTF-8", - Position: Position{Line: 1, Start: i, Len: 1}, + Position: Position{Line: 1, Col: 1, Start: i, Len: 1}, Line: 1, input: data, } @@ -92,8 +92,9 @@ func parse(data string) (p *parser, err error) { func (p *parser) panicErr(it item, err error) { panic(ParseError{ + Message: err.Error(), err: err, - Position: it.pos, + Position: it.pos.withCol(p.lx.input), Line: it.pos.Len, LastKey: p.current(), }) @@ -102,7 +103,7 @@ func (p *parser) panicErr(it item, err error) { func (p *parser) panicItemf(it item, format string, v ...any) { panic(ParseError{ Message: fmt.Sprintf(format, v...), - Position: it.pos, + Position: it.pos.withCol(p.lx.input), Line: it.pos.Len, LastKey: p.current(), }) @@ -111,7 +112,7 @@ func (p *parser) panicItemf(it item, format string, v ...any) { func (p *parser) panicf(format string, v ...any) { panic(ParseError{ Message: fmt.Sprintf(format, v...), - Position: p.pos, + Position: p.pos.withCol(p.lx.input), Line: p.pos.Line, LastKey: p.current(), }) @@ -123,10 +124,11 @@ func (p *parser) next() item { if it.typ == itemError { if it.err != nil { panic(ParseError{ - Position: it.pos, + Message: it.err.Error(), + err: it.err, + Position: it.pos.withCol(p.lx.input), Line: it.pos.Line, LastKey: p.current(), - err: it.err, }) } diff --git a/tools/vendor/github.com/Crocmagnon/fatcontext/pkg/analyzer/analyzer.go b/tools/vendor/github.com/Crocmagnon/fatcontext/pkg/analyzer/analyzer.go index 7bb8fd4282..a65efbba89 100644 --- a/tools/vendor/github.com/Crocmagnon/fatcontext/pkg/analyzer/analyzer.go +++ b/tools/vendor/github.com/Crocmagnon/fatcontext/pkg/analyzer/analyzer.go @@ -7,6 +7,8 @@ import ( "go/ast" "go/printer" "go/token" + "go/types" + "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis/passes/inspect" "golang.org/x/tools/go/ast/inspector" @@ -14,7 +16,7 @@ import ( var Analyzer = &analysis.Analyzer{ Name: "fatcontext", - Doc: "detects nested contexts in loops", + Doc: "detects nested contexts in loops and function literals", Run: run, Requires: []*analysis.Analyzer{inspect.Analyzer}, } @@ -27,6 +29,7 @@ func run(pass *analysis.Pass) (interface{}, error) { nodeFilter := []ast.Node{ (*ast.ForStmt)(nil), (*ast.RangeStmt)(nil), + (*ast.FuncLit)(nil), } inspctr.Preorder(nodeFilter, func(node ast.Node) { @@ -35,60 +38,54 @@ func run(pass *analysis.Pass) (interface{}, error) { return } - for _, stmt := range body.List { - assignStmt, ok := stmt.(*ast.AssignStmt) - if !ok { - continue - } - - t := pass.TypesInfo.TypeOf(assignStmt.Lhs[0]) - if t == nil { - continue - } - - if t.String() != "context.Context" { - continue - } - - if assignStmt.Tok == token.DEFINE { - break - } + assignStmt := findNestedContext(pass, node, body.List) + if assignStmt == nil { + return + } - suggestedStmt := ast.AssignStmt{ - Lhs: assignStmt.Lhs, - TokPos: assignStmt.TokPos, - Tok: token.DEFINE, - Rhs: assignStmt.Rhs, - } - suggested, err := render(pass.Fset, &suggestedStmt) - - var fixes []analysis.SuggestedFix - if err == nil { - fixes = append(fixes, analysis.SuggestedFix{ - Message: "replace `=` with `:=`", - TextEdits: []analysis.TextEdit{ - { - Pos: assignStmt.Pos(), - End: assignStmt.End(), - NewText: []byte(suggested), - }, + suggestedStmt := ast.AssignStmt{ + Lhs: assignStmt.Lhs, + TokPos: assignStmt.TokPos, + Tok: token.DEFINE, + Rhs: assignStmt.Rhs, + } + suggested, err := render(pass.Fset, &suggestedStmt) + + var fixes []analysis.SuggestedFix + if err == nil { + fixes = append(fixes, analysis.SuggestedFix{ + Message: "replace `=` with `:=`", + TextEdits: []analysis.TextEdit{ + { + Pos: assignStmt.Pos(), + End: assignStmt.End(), + NewText: []byte(suggested), }, - }) - } - - pass.Report(analysis.Diagnostic{ - Pos: assignStmt.Pos(), - Message: "nested context in loop", - SuggestedFixes: fixes, + }, }) - - break } + + pass.Report(analysis.Diagnostic{ + Pos: assignStmt.Pos(), + Message: getReportMessage(node), + SuggestedFixes: fixes, + }) }) return nil, nil } +func getReportMessage(node ast.Node) string { + switch node.(type) { + case *ast.ForStmt, *ast.RangeStmt: + return "nested context in loop" + case *ast.FuncLit: + return "nested context in function literal" + default: + return "unsupported nested context type" + } +} + func getBody(node ast.Node) (*ast.BlockStmt, error) { forStmt, ok := node.(*ast.ForStmt) if ok { @@ -100,9 +97,123 @@ func getBody(node ast.Node) (*ast.BlockStmt, error) { return rangeStmt.Body, nil } + funcLit, ok := node.(*ast.FuncLit) + if ok { + return funcLit.Body, nil + } + return nil, errUnknown } +func findNestedContext(pass *analysis.Pass, node ast.Node, stmts []ast.Stmt) *ast.AssignStmt { + for _, stmt := range stmts { + // Recurse if necessary + if inner, ok := stmt.(*ast.BlockStmt); ok { + found := findNestedContext(pass, node, inner.List) + if found != nil { + return found + } + } + + if inner, ok := stmt.(*ast.IfStmt); ok { + found := findNestedContext(pass, node, inner.Body.List) + if found != nil { + return found + } + } + + if inner, ok := stmt.(*ast.SwitchStmt); ok { + found := findNestedContext(pass, node, inner.Body.List) + if found != nil { + return found + } + } + + if inner, ok := stmt.(*ast.CaseClause); ok { + found := findNestedContext(pass, node, inner.Body) + if found != nil { + return found + } + } + + if inner, ok := stmt.(*ast.SelectStmt); ok { + found := findNestedContext(pass, node, inner.Body.List) + if found != nil { + return found + } + } + + if inner, ok := stmt.(*ast.CommClause); ok { + found := findNestedContext(pass, node, inner.Body) + if found != nil { + return found + } + } + + // Actually check for nested context + assignStmt, ok := stmt.(*ast.AssignStmt) + if !ok { + continue + } + + t := pass.TypesInfo.TypeOf(assignStmt.Lhs[0]) + if t == nil { + continue + } + + if t.String() != "context.Context" { + continue + } + + if assignStmt.Tok == token.DEFINE { + continue + } + + // allow assignment to non-pointer children of values defined within the loop + if lhs := getRootIdent(pass, assignStmt.Lhs[0]); lhs != nil { + if obj := pass.TypesInfo.ObjectOf(lhs); obj != nil { + if checkObjectScopeWithinNode(obj.Parent(), node) { + continue // definition is within the loop + } + } + } + + return assignStmt + } + + return nil +} + +func checkObjectScopeWithinNode(scope *types.Scope, node ast.Node) bool { + if scope == nil { + return false + } + + if scope.Pos() >= node.Pos() && scope.End() <= node.End() { + return true + } + + return false +} + +func getRootIdent(pass *analysis.Pass, node ast.Node) *ast.Ident { + for { + switch n := node.(type) { + case *ast.Ident: + return n + case *ast.IndexExpr: + node = n.X + case *ast.SelectorExpr: + if sel, ok := pass.TypesInfo.Selections[n]; ok && sel.Indirect() { + return nil // indirected (pointer) roots don't imply a (safe) copy + } + node = n.X + default: + return nil + } + } +} + // render returns the pretty-print of the given node func render(fset *token.FileSet, x interface{}) (string, error) { var buf bytes.Buffer diff --git a/tools/vendor/github.com/GaijinEntertainment/go-exhaustruct/v3/analyzer/analyzer.go b/tools/vendor/github.com/GaijinEntertainment/go-exhaustruct/v3/analyzer/analyzer.go index b490f1c640..ec75fd4090 100644 --- a/tools/vendor/github.com/GaijinEntertainment/go-exhaustruct/v3/analyzer/analyzer.go +++ b/tools/vendor/github.com/GaijinEntertainment/go-exhaustruct/v3/analyzer/analyzer.go @@ -100,12 +100,9 @@ func (a *analyzer) newVisitor(pass *analysis.Pass) func(n ast.Node, push bool, s if len(lit.Elts) == 0 { if ret, ok := stackParentIsReturn(stack); ok { - if returnContainsNonNilError(pass, ret) { + if returnContainsNonNilError(pass, ret, n) { // it is okay to return uninitialized structure in case struct's direct parent is // a return statement containing non-nil error - // - // we're unable to check if returned error is custom, but at least we're able to - // cover str [error] type. return true } } @@ -184,17 +181,47 @@ func getStructType(pass *analysis.Pass, lit *ast.CompositeLit) (*types.Struct, * func stackParentIsReturn(stack []ast.Node) (*ast.ReturnStmt, bool) { // it is safe to skip boundary check, since stack always has at least one element - // - whole file. - ret, ok := stack[len(stack)-2].(*ast.ReturnStmt) + // we also have no reason to check the first element, since it is always a file + for i := len(stack) - 2; i > 0; i-- { + switch st := stack[i].(type) { + case *ast.ReturnStmt: + return st, true - return ret, ok + case *ast.UnaryExpr: + // in case we're dealing with pointers - it is still viable to check pointer's + // parent for return statement + continue + + default: + return nil, false + } + } + + return nil, false } -func returnContainsNonNilError(pass *analysis.Pass, ret *ast.ReturnStmt) bool { +// errorIface is a type that represents [error] interface and all types will be +// compared against. +var errorIface = types.Universe.Lookup("error").Type().Underlying().(*types.Interface) + +func returnContainsNonNilError(pass *analysis.Pass, ret *ast.ReturnStmt, except ast.Node) bool { // errors are mostly located at the end of return statement, so we're starting // from the end. for i := len(ret.Results) - 1; i >= 0; i-- { - if pass.TypesInfo.TypeOf(ret.Results[i]).String() == "error" { + ri := ret.Results[i] + + // skip current node + if ri == except { + continue + } + + if un, ok := ri.(*ast.UnaryExpr); ok { + if un.X == except { + continue + } + } + + if types.Implements(pass.TypesInfo.TypeOf(ri), errorIface) { return true } } diff --git a/tools/vendor/github.com/Masterminds/semver/v3/CHANGELOG.md b/tools/vendor/github.com/Masterminds/semver/v3/CHANGELOG.md index f12626423a..f95a504fe7 100644 --- a/tools/vendor/github.com/Masterminds/semver/v3/CHANGELOG.md +++ b/tools/vendor/github.com/Masterminds/semver/v3/CHANGELOG.md @@ -1,5 +1,33 @@ # Changelog +## 3.3.0 (2024-08-27) + +### Added + +- #238: Add LessThanEqual and GreaterThanEqual functions (thanks @grosser) +- #213: nil version equality checking (thanks @KnutZuidema) + +### Changed + +- #241: Simplify StrictNewVersion parsing (thanks @grosser) +- Testing support up through Go 1.23 +- Minimum version set to 1.21 as this is what's tested now +- Fuzz testing now supports caching + +## 3.2.1 (2023-04-10) + +### Changed + +- #198: Improved testing around pre-release names +- #200: Improved code scanning with addition of CodeQL +- #201: Testing now includes Go 1.20. Go 1.17 has been dropped +- #202: Migrated Fuzz testing to Go built-in Fuzzing. CI runs daily +- #203: Docs updated for security details + +### Fixed + +- #199: Fixed issue with range transformations + ## 3.2.0 (2022-11-28) ### Added diff --git a/tools/vendor/github.com/Masterminds/semver/v3/Makefile b/tools/vendor/github.com/Masterminds/semver/v3/Makefile index 0e7b5c7138..9ca87a2c79 100644 --- a/tools/vendor/github.com/Masterminds/semver/v3/Makefile +++ b/tools/vendor/github.com/Masterminds/semver/v3/Makefile @@ -19,6 +19,7 @@ test-cover: .PHONY: fuzz fuzz: @echo "==> Running Fuzz Tests" + go env GOCACHE go test -fuzz=FuzzNewVersion -fuzztime=15s . go test -fuzz=FuzzStrictNewVersion -fuzztime=15s . go test -fuzz=FuzzNewConstraint -fuzztime=15s . @@ -27,4 +28,4 @@ $(GOLANGCI_LINT): # Install golangci-lint. The configuration for it is in the .golangci.yml # file in the root of the repository echo ${GOPATH} - curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(GOPATH)/bin v1.17.1 + curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(GOPATH)/bin v1.56.2 diff --git a/tools/vendor/github.com/Masterminds/semver/v3/README.md b/tools/vendor/github.com/Masterminds/semver/v3/README.md index eab8cac3b7..ed56936084 100644 --- a/tools/vendor/github.com/Masterminds/semver/v3/README.md +++ b/tools/vendor/github.com/Masterminds/semver/v3/README.md @@ -13,12 +13,9 @@ Active](https://masterminds.github.io/stability/active.svg)](https://masterminds [![GoDoc](https://img.shields.io/static/v1?label=godoc&message=reference&color=blue)](https://pkg.go.dev/github.com/Masterminds/semver/v3) [![Go Report Card](https://goreportcard.com/badge/github.com/Masterminds/semver)](https://goreportcard.com/report/github.com/Masterminds/semver) -If you are looking for a command line tool for version comparisons please see -[vert](https://github.com/Masterminds/vert) which uses this library. - ## Package Versions -Note, import `github.com/github.com/Masterminds/semver/v3` to use the latest version. +Note, import `github.com/Masterminds/semver/v3` to use the latest version. There are three major versions fo the `semver` package. @@ -80,12 +77,12 @@ There are two methods for comparing versions. One uses comparison methods on differences to notes between these two methods of comparison. 1. When two versions are compared using functions such as `Compare`, `LessThan`, - and others it will follow the specification and always include prereleases + and others it will follow the specification and always include pre-releases within the comparison. It will provide an answer that is valid with the comparison section of the spec at https://semver.org/#spec-item-11 2. When constraint checking is used for checks or validation it will follow a different set of rules that are common for ranges with tools like npm/js - and Rust/Cargo. This includes considering prereleases to be invalid if the + and Rust/Cargo. This includes considering pre-releases to be invalid if the ranges does not include one. If you want to have it include pre-releases a simple solution is to include `-0` in your range. 3. Constraint ranges can have some complex rules including the shorthand use of @@ -113,7 +110,7 @@ v, err := semver.NewVersion("1.3") if err != nil { // Handle version not being parsable. } -// Check if the version meets the constraints. The a variable will be true. +// Check if the version meets the constraints. The variable a will be true. a := c.Check(v) ``` @@ -137,20 +134,20 @@ The basic comparisons are: ### Working With Prerelease Versions Pre-releases, for those not familiar with them, are used for software releases -prior to stable or generally available releases. Examples of prereleases include -development, alpha, beta, and release candidate releases. A prerelease may be +prior to stable or generally available releases. Examples of pre-releases include +development, alpha, beta, and release candidate releases. A pre-release may be a version such as `1.2.3-beta.1` while the stable release would be `1.2.3`. In the -order of precedence, prereleases come before their associated releases. In this +order of precedence, pre-releases come before their associated releases. In this example `1.2.3-beta.1 < 1.2.3`. -According to the Semantic Version specification prereleases may not be +According to the Semantic Version specification, pre-releases may not be API compliant with their release counterpart. It says, > A pre-release version indicates that the version is unstable and might not satisfy the intended compatibility requirements as denoted by its associated normal version. -SemVer comparisons using constraints without a prerelease comparator will skip -prerelease versions. For example, `>=1.2.3` will skip prereleases when looking -at a list of releases while `>=1.2.3-0` will evaluate and find prereleases. +SemVer's comparisons using constraints without a pre-release comparator will skip +pre-release versions. For example, `>=1.2.3` will skip pre-releases when looking +at a list of releases while `>=1.2.3-0` will evaluate and find pre-releases. The reason for the `0` as a pre-release version in the example comparison is because pre-releases can only contain ASCII alphanumerics and hyphens (along with @@ -171,6 +168,9 @@ These look like: * `1.2 - 1.4.5` which is equivalent to `>= 1.2 <= 1.4.5` * `2.3.4 - 4.5` which is equivalent to `>= 2.3.4 <= 4.5` +Note that `1.2-1.4.5` without whitespace is parsed completely differently; it's +parsed as a single constraint `1.2.0` with _prerelease_ `1.4.5`. + ### Wildcards In Comparisons The `x`, `X`, and `*` characters can be used as a wildcard character. This works diff --git a/tools/vendor/github.com/Masterminds/semver/v3/version.go b/tools/vendor/github.com/Masterminds/semver/v3/version.go index 7c4bed3347..ff499fb664 100644 --- a/tools/vendor/github.com/Masterminds/semver/v3/version.go +++ b/tools/vendor/github.com/Masterminds/semver/v3/version.go @@ -83,22 +83,23 @@ func StrictNewVersion(v string) (*Version, error) { original: v, } - // check for prerelease or build metadata - var extra []string - if strings.ContainsAny(parts[2], "-+") { - // Start with the build metadata first as it needs to be on the right - extra = strings.SplitN(parts[2], "+", 2) - if len(extra) > 1 { - // build metadata found - sv.metadata = extra[1] - parts[2] = extra[0] + // Extract build metadata + if strings.Contains(parts[2], "+") { + extra := strings.SplitN(parts[2], "+", 2) + sv.metadata = extra[1] + parts[2] = extra[0] + if err := validateMetadata(sv.metadata); err != nil { + return nil, err } + } - extra = strings.SplitN(parts[2], "-", 2) - if len(extra) > 1 { - // prerelease found - sv.pre = extra[1] - parts[2] = extra[0] + // Extract build prerelease + if strings.Contains(parts[2], "-") { + extra := strings.SplitN(parts[2], "-", 2) + sv.pre = extra[1] + parts[2] = extra[0] + if err := validatePrerelease(sv.pre); err != nil { + return nil, err } } @@ -114,7 +115,7 @@ func StrictNewVersion(v string) (*Version, error) { } } - // Extract the major, minor, and patch elements onto the returned Version + // Extract major, minor, and patch var err error sv.major, err = strconv.ParseUint(parts[0], 10, 64) if err != nil { @@ -131,23 +132,6 @@ func StrictNewVersion(v string) (*Version, error) { return nil, err } - // No prerelease or build metadata found so returning now as a fastpath. - if sv.pre == "" && sv.metadata == "" { - return sv, nil - } - - if sv.pre != "" { - if err = validatePrerelease(sv.pre); err != nil { - return nil, err - } - } - - if sv.metadata != "" { - if err = validateMetadata(sv.metadata); err != nil { - return nil, err - } - } - return sv, nil } @@ -381,15 +365,31 @@ func (v *Version) LessThan(o *Version) bool { return v.Compare(o) < 0 } +// LessThanEqual tests if one version is less or equal than another one. +func (v *Version) LessThanEqual(o *Version) bool { + return v.Compare(o) <= 0 +} + // GreaterThan tests if one version is greater than another one. func (v *Version) GreaterThan(o *Version) bool { return v.Compare(o) > 0 } +// GreaterThanEqual tests if one version is greater or equal than another one. +func (v *Version) GreaterThanEqual(o *Version) bool { + return v.Compare(o) >= 0 +} + // Equal tests if two versions are equal to each other. // Note, versions can be equal with different metadata since metadata // is not considered part of the comparable version. func (v *Version) Equal(o *Version) bool { + if v == o { + return true + } + if v == nil || o == nil { + return false + } return v.Compare(o) == 0 } diff --git a/tools/vendor/github.com/bombsimon/wsl/v4/.gitignore b/tools/vendor/github.com/bombsimon/wsl/v4/.gitignore index 1c8eba613e..b37c694812 100644 --- a/tools/vendor/github.com/bombsimon/wsl/v4/.gitignore +++ b/tools/vendor/github.com/bombsimon/wsl/v4/.gitignore @@ -68,3 +68,5 @@ tags # End of https://www.gitignore.io/api/go,vim,macos + +.idea/ diff --git a/tools/vendor/github.com/bombsimon/wsl/v4/.golangci.yml b/tools/vendor/github.com/bombsimon/wsl/v4/.golangci.yml index 543012008f..bde0ae54e4 100644 --- a/tools/vendor/github.com/bombsimon/wsl/v4/.golangci.yml +++ b/tools/vendor/github.com/bombsimon/wsl/v4/.golangci.yml @@ -1,24 +1,25 @@ --- run: - deadline: 1m + timeout: 1m issues-exit-code: 1 tests: true - skip-dirs: - - vendor$ output: - format: colored-line-number print-issued-lines: false + sort-results: true + formats: + - format: colored-line-number linters-settings: gocognit: min-complexity: 10 depguard: - list-type: blacklist - include-go-root: false - packages: - - github.com/davecgh/go-spew/spew + rules: + main: + deny: + - pkg: "github.com/davecgh/go-spew/spew" + desc: not allowed misspell: locale: US @@ -70,8 +71,6 @@ linters: - varcheck - varnamelen - wastedassign - fast: false - issues: exclude-use-default: true diff --git a/tools/vendor/github.com/bombsimon/wsl/v4/README.md b/tools/vendor/github.com/bombsimon/wsl/v4/README.md index 0bcf01d96a..c9c42341ef 100644 --- a/tools/vendor/github.com/bombsimon/wsl/v4/README.md +++ b/tools/vendor/github.com/bombsimon/wsl/v4/README.md @@ -20,7 +20,7 @@ make something configurable! ```sh # Latest release -go install github.com/bombsimon/wsl/v4/cmd/wsl +go install github.com/bombsimon/wsl/v4/cmd/wsl@latest # Main branch go install github.com/bombsimon/wsl/v4/cmd/wsl@master diff --git a/tools/vendor/github.com/bombsimon/wsl/v4/analyzer.go b/tools/vendor/github.com/bombsimon/wsl/v4/analyzer.go index b8eac15875..46d5019a78 100644 --- a/tools/vendor/github.com/bombsimon/wsl/v4/analyzer.go +++ b/tools/vendor/github.com/bombsimon/wsl/v4/analyzer.go @@ -2,6 +2,7 @@ package wsl import ( "flag" + "go/ast" "strings" "golang.org/x/tools/go/analysis" @@ -30,6 +31,7 @@ func defaultConfig() *Configuration { ForceCuddleErrCheckAndAssign: false, ForceExclusiveShortDeclarations: false, StrictAppend: true, + IncludeGenerated: false, AllowCuddleWithCalls: []string{"Lock", "RLock"}, AllowCuddleWithRHS: []string{"Unlock", "RUnlock"}, ErrorVariableNames: []string{"err"}, @@ -64,6 +66,7 @@ func (wa *wslAnalyzer) flags() flag.FlagSet { flags.BoolVar(&wa.config.ForceCuddleErrCheckAndAssign, "force-err-cuddling", false, "Force cuddling of error checks with error var assignment") flags.BoolVar(&wa.config.ForceExclusiveShortDeclarations, "force-short-decl-cuddling", false, "Force short declarations to cuddle by themselves") flags.BoolVar(&wa.config.StrictAppend, "strict-append", true, "Strict rules for append") + flags.BoolVar(&wa.config.IncludeGenerated, "include-generated", false, "Include generated files") flags.IntVar(&wa.config.ForceCaseTrailingWhitespaceLimit, "force-case-trailing-whitespace", 0, "Force newlines for case blocks > this number.") flags.Var(&multiStringValue{slicePtr: &wa.config.AllowCuddleWithCalls}, "allow-cuddle-with-calls", "Comma separated list of idents that can have cuddles after") @@ -75,6 +78,10 @@ func (wa *wslAnalyzer) flags() flag.FlagSet { func (wa *wslAnalyzer) run(pass *analysis.Pass) (interface{}, error) { for _, file := range pass.Files { + if !wa.config.IncludeGenerated && ast.IsGenerated(file) { + continue + } + filename := pass.Fset.PositionFor(file.Pos(), false).Filename if !strings.HasSuffix(filename, ".go") { continue diff --git a/tools/vendor/github.com/bombsimon/wsl/v4/wsl.go b/tools/vendor/github.com/bombsimon/wsl/v4/wsl.go index 6fd33335a1..76f4abf617 100644 --- a/tools/vendor/github.com/bombsimon/wsl/v4/wsl.go +++ b/tools/vendor/github.com/bombsimon/wsl/v4/wsl.go @@ -174,6 +174,11 @@ type Configuration struct { // // is not allowed. This logic overrides ForceCuddleErrCheckAndAssign among others. ForceExclusiveShortDeclarations bool + + // IncludeGenerated will include generated files in the analysis and report + // errors even for generated files. Can be useful when developing + // generators. + IncludeGenerated bool } // fix is a range to fixup. @@ -578,7 +583,7 @@ func (p *processor) parseBlockStatements(statements []ast.Stmt) { } p.addWhitespaceBeforeError(t, reasonExpressionCuddledWithDeclOrRet) - case *ast.IfStmt, *ast.RangeStmt, *ast.SwitchStmt: + case *ast.IfStmt, *ast.RangeStmt, *ast.SwitchStmt, *ast.TypeSwitchStmt, *ast.ForStmt: p.addWhitespaceBeforeError(t, reasonExpressionCuddledWithBlock) } diff --git a/tools/vendor/github.com/ckaznocha/intrange/go.work b/tools/vendor/github.com/ckaznocha/intrange/go.work index f41a04a2fb..3814c99f95 100644 --- a/tools/vendor/github.com/ckaznocha/intrange/go.work +++ b/tools/vendor/github.com/ckaznocha/intrange/go.work @@ -1,4 +1,4 @@ -go 1.22.0 +go 1.22 use ( . diff --git a/tools/vendor/github.com/ckaznocha/intrange/intrange.go b/tools/vendor/github.com/ckaznocha/intrange/intrange.go index fac4e3deae..33cddf3035 100644 --- a/tools/vendor/github.com/ckaznocha/intrange/intrange.go +++ b/tools/vendor/github.com/ckaznocha/intrange/intrange.go @@ -5,6 +5,7 @@ import ( "fmt" "go/ast" "go/token" + "go/types" "strconv" "golang.org/x/tools/go/analysis" @@ -23,7 +24,10 @@ var ( errFailedAnalysis = errors.New("failed analysis") ) -const msg = "for loop can be changed to use an integer range (Go 1.22+)" +const ( + msg = "for loop can be changed to use an integer range (Go 1.22+)" + msgLenRange = "for loop can be changed to `i := range %s`" +) func run(pass *analysis.Pass) (any, error) { result, ok := pass.ResultOf[inspect.Analyzer] @@ -44,90 +48,134 @@ func run(pass *analysis.Pass) (any, error) { ) } - resultInspector.Preorder([]ast.Node{(*ast.ForStmt)(nil)}, check(pass)) + resultInspector.Preorder([]ast.Node{(*ast.ForStmt)(nil), (*ast.RangeStmt)(nil)}, check(pass)) return nil, nil } func check(pass *analysis.Pass) func(node ast.Node) { return func(node ast.Node) { - forStmt, ok := node.(*ast.ForStmt) - if !ok { + switch stmt := node.(type) { + case *ast.ForStmt: + checkForStmt(pass, stmt) + case *ast.RangeStmt: + checkRangeStmt(pass, stmt) + default: return } + } +} + +func checkForStmt(pass *analysis.Pass, forStmt *ast.ForStmt) { + // Existing checks for other patterns + if forStmt.Init == nil || forStmt.Cond == nil || forStmt.Post == nil { + return + } + + // i := 0;; + init, ok := forStmt.Init.(*ast.AssignStmt) + if !ok { + return + } + + if len(init.Lhs) != 1 || len(init.Rhs) != 1 { + return + } + + initIdent, ok := init.Lhs[0].(*ast.Ident) + if !ok { + return + } + + if !compareNumberLit(init.Rhs[0], 0) { + return + } + + cond, ok := forStmt.Cond.(*ast.BinaryExpr) + if !ok { + return + } - if forStmt.Init == nil || forStmt.Cond == nil || forStmt.Post == nil { + var nExpr ast.Expr + + switch cond.Op { + case token.LSS: // ;i < n; + if isBenchmark(cond.Y) { return } - // i := 0;; - init, ok := forStmt.Init.(*ast.AssignStmt) + nExpr = findNExpr(cond.Y) + + x, ok := cond.X.(*ast.Ident) if !ok { return } - if len(init.Lhs) != 1 || len(init.Rhs) != 1 { + if x.Name != initIdent.Name { + return + } + case token.GTR: // ;n > i; + if isBenchmark(cond.X) { return } - initIdent, ok := init.Lhs[0].(*ast.Ident) + nExpr = findNExpr(cond.X) + + y, ok := cond.Y.(*ast.Ident) if !ok { return } - if !compareNumberLit(init.Rhs[0], 0) { + if y.Name != initIdent.Name { return } + default: + return + } - cond, ok := forStmt.Cond.(*ast.BinaryExpr) - if !ok { + switch post := forStmt.Post.(type) { + case *ast.IncDecStmt: // ;;i++ + if post.Tok != token.INC { return } - var nExpr ast.Expr + ident, ok := post.X.(*ast.Ident) + if !ok { + return + } - switch cond.Op { - case token.LSS: // ;i < n; - if isBenchmark(cond.Y) { + if ident.Name != initIdent.Name { + return + } + case *ast.AssignStmt: + switch post.Tok { + case token.ADD_ASSIGN: // ;;i += 1 + if len(post.Lhs) != 1 { return } - nExpr = findNExpr(cond.Y) - - x, ok := cond.X.(*ast.Ident) + ident, ok := post.Lhs[0].(*ast.Ident) if !ok { return } - if x.Name != initIdent.Name { - return - } - case token.GTR: // ;n > i; - if isBenchmark(cond.X) { + if ident.Name != initIdent.Name { return } - nExpr = findNExpr(cond.X) - - y, ok := cond.Y.(*ast.Ident) - if !ok { + if len(post.Rhs) != 1 { return } - if y.Name != initIdent.Name { + if !compareNumberLit(post.Rhs[0], 1) { return } - default: - return - } - - switch post := forStmt.Post.(type) { - case *ast.IncDecStmt: // ;;i++ - if post.Tok != token.INC { + case token.ASSIGN: // ;;i = i + 1 && ;;i = 1 + i + if len(post.Lhs) != 1 || len(post.Rhs) != 1 { return } - ident, ok := post.X.(*ast.Ident) + ident, ok := post.Lhs[0].(*ast.Ident) if !ok { return } @@ -135,35 +183,31 @@ func check(pass *analysis.Pass) func(node ast.Node) { if ident.Name != initIdent.Name { return } - case *ast.AssignStmt: - switch post.Tok { - case token.ADD_ASSIGN: // ;;i += 1 - if len(post.Lhs) != 1 { - return - } - ident, ok := post.Lhs[0].(*ast.Ident) - if !ok { - return - } + bin, ok := post.Rhs[0].(*ast.BinaryExpr) + if !ok { + return + } - if ident.Name != initIdent.Name { - return - } + if bin.Op != token.ADD { + return + } - if len(post.Rhs) != 1 { + switch x := bin.X.(type) { + case *ast.Ident: // ;;i = i + 1 + if x.Name != initIdent.Name { return } - if !compareNumberLit(post.Rhs[0], 1) { + if !compareNumberLit(bin.Y, 1) { return } - case token.ASSIGN: // ;;i = i + 1 && ;;i = 1 + i - if len(post.Lhs) != 1 || len(post.Rhs) != 1 { + case *ast.BasicLit: // ;;i = 1 + i + if !compareNumberLit(x, 1) { return } - ident, ok := post.Lhs[0].(*ast.Ident) + ident, ok := bin.Y.(*ast.Ident) if !ok { return } @@ -171,64 +215,103 @@ func check(pass *analysis.Pass) func(node ast.Node) { if ident.Name != initIdent.Name { return } - - bin, ok := post.Rhs[0].(*ast.BinaryExpr) - if !ok { - return - } - - if bin.Op != token.ADD { - return - } - - switch x := bin.X.(type) { - case *ast.Ident: // ;;i = i + 1 - if x.Name != initIdent.Name { - return - } - - if !compareNumberLit(bin.Y, 1) { - return - } - case *ast.BasicLit: // ;;i = 1 + i - if !compareNumberLit(x, 1) { - return - } - - ident, ok := bin.Y.(*ast.Ident) - if !ok { - return - } - - if ident.Name != initIdent.Name { - return - } - default: - return - } default: return } default: return } + default: + return + } - bc := &bodyChecker{ - initIdent: initIdent, - nExpr: nExpr, - } + bc := &bodyChecker{ + initIdent: initIdent, + nExpr: nExpr, + } - ast.Inspect(forStmt.Body, bc.check) + ast.Inspect(forStmt.Body, bc.check) - if bc.modified { - return - } + if bc.modified { + return + } - pass.Report(analysis.Diagnostic{ - Pos: forStmt.Pos(), - Message: msg, - }) + pass.Report(analysis.Diagnostic{ + Pos: forStmt.Pos(), + Message: msg, + }) +} + +func checkRangeStmt(pass *analysis.Pass, rangeStmt *ast.RangeStmt) { + if rangeStmt.Key == nil { + return } + + ident, ok := rangeStmt.Key.(*ast.Ident) + if !ok { + return + } + + if ident.Name == "_" { + return + } + + if rangeStmt.Value != nil { + return + } + + if rangeStmt.X == nil { + return + } + + x, ok := rangeStmt.X.(*ast.CallExpr) + if !ok { + return + } + + fn, ok := x.Fun.(*ast.Ident) + if !ok { + return + } + + if fn.Name != "len" || len(x.Args) != 1 { + return + } + + arg, ok := x.Args[0].(*ast.Ident) + if !ok { + return + } + + // make sure arg is a slice or array + obj := pass.TypesInfo.ObjectOf(arg) + if obj == nil { + return + } + + switch obj.Type().Underlying().(type) { + case *types.Slice, *types.Array: + default: + return + } + + pass.Report(analysis.Diagnostic{ + Pos: ident.Pos(), + End: x.End(), + Message: fmt.Sprintf(msgLenRange, arg.Name), + SuggestedFixes: []analysis.SuggestedFix{ + { + Message: fmt.Sprintf("Replace `len(%s)` with `%s`", arg.Name, arg.Name), + TextEdits: []analysis.TextEdit{ + { + Pos: x.Pos(), + End: x.End(), + NewText: []byte(arg.Name), + }, + }, + }, + }, + }) } func findNExpr(expr ast.Expr) ast.Expr { diff --git a/tools/vendor/github.com/daixiang0/gci/pkg/config/config.go b/tools/vendor/github.com/daixiang0/gci/pkg/config/config.go index cc43f2fa07..814201a006 100644 --- a/tools/vendor/github.com/daixiang0/gci/pkg/config/config.go +++ b/tools/vendor/github.com/daixiang0/gci/pkg/config/config.go @@ -26,6 +26,7 @@ type BoolConfig struct { SkipGenerated bool `yaml:"skipGenerated"` SkipVendor bool `yaml:"skipVendor"` CustomOrder bool `yaml:"customOrder"` + NoLexOrder bool `yaml:"noLexOrder"` } type Config struct { @@ -63,10 +64,11 @@ func (g YamlConfig) Parse() (*Config, error) { sort.Slice(sections, func(i, j int) bool { sectionI, sectionJ := sections[i].Type(), sections[j].Type() - if strings.Compare(sectionI, sectionJ) == 0 { - return strings.Compare(sections[i].String(), sections[j].String()) < 0 + if g.Cfg.NoLexOrder || strings.Compare(sectionI, sectionJ) != 0 { + return defaultOrder[sectionI] < defaultOrder[sectionJ] } - return defaultOrder[sectionI] < defaultOrder[sectionJ] + + return strings.Compare(sections[i].String(), sections[j].String()) < 0 }) } diff --git a/tools/vendor/github.com/daixiang0/gci/pkg/section/standard_list.go b/tools/vendor/github.com/daixiang0/gci/pkg/section/standard_list.go index a2cd0a6dd2..5a2dcdc899 100644 --- a/tools/vendor/github.com/daixiang0/gci/pkg/section/standard_list.go +++ b/tools/vendor/github.com/daixiang0/gci/pkg/section/standard_list.go @@ -1,6 +1,6 @@ package section -// Code generated based on go1.22.0 X:boringcrypto,arenas. DO NOT EDIT. +// Code generated based on go1.23.0 X:boringcrypto,arenas. DO NOT EDIT. var standardPackages = map[string]struct{}{ "archive/tar": {}, @@ -100,6 +100,7 @@ var standardPackages = map[string]struct{}{ "io": {}, "io/fs": {}, "io/ioutil": {}, + "iter": {}, "log": {}, "log/slog": {}, "log/syslog": {}, @@ -151,6 +152,7 @@ var standardPackages = map[string]struct{}{ "sort": {}, "strconv": {}, "strings": {}, + "structs": {}, "sync": {}, "sync/atomic": {}, "syscall": {}, @@ -168,5 +170,6 @@ var standardPackages = map[string]struct{}{ "unicode": {}, "unicode/utf16": {}, "unicode/utf8": {}, + "unique": {}, "unsafe": {}, } diff --git a/tools/vendor/github.com/go-viper/mapstructure/v2/decode_hooks.go b/tools/vendor/github.com/go-viper/mapstructure/v2/decode_hooks.go index 24d82f07c3..2523c6ad98 100644 --- a/tools/vendor/github.com/go-viper/mapstructure/v2/decode_hooks.go +++ b/tools/vendor/github.com/go-viper/mapstructure/v2/decode_hooks.go @@ -36,6 +36,30 @@ func typedDecodeHook(h DecodeHookFunc) DecodeHookFunc { return nil } +// cachedDecodeHook takes a raw DecodeHookFunc (an interface{}) and turns +// it into a closure to be used directly +// if the type fails to convert we return a closure always erroring to keep the previous behaviour +func cachedDecodeHook(raw DecodeHookFunc) func(from reflect.Value, to reflect.Value) (interface{}, error) { + switch f := typedDecodeHook(raw).(type) { + case DecodeHookFuncType: + return func(from reflect.Value, to reflect.Value) (interface{}, error) { + return f(from.Type(), to.Type(), from.Interface()) + } + case DecodeHookFuncKind: + return func(from reflect.Value, to reflect.Value) (interface{}, error) { + return f(from.Kind(), to.Kind(), from.Interface()) + } + case DecodeHookFuncValue: + return func(from reflect.Value, to reflect.Value) (interface{}, error) { + return f(from, to) + } + default: + return func(from reflect.Value, to reflect.Value) (interface{}, error) { + return nil, errors.New("invalid decode hook signature") + } + } +} + // DecodeHookExec executes the given decode hook. This should be used // since it'll naturally degrade to the older backwards compatible DecodeHookFunc // that took reflect.Kind instead of reflect.Type. @@ -61,13 +85,17 @@ func DecodeHookExec( // The composed funcs are called in order, with the result of the // previous transformation. func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc { + cached := make([]func(from reflect.Value, to reflect.Value) (interface{}, error), 0, len(fs)) + for _, f := range fs { + cached = append(cached, cachedDecodeHook(f)) + } return func(f reflect.Value, t reflect.Value) (interface{}, error) { var err error data := f.Interface() newFrom := f - for _, f1 := range fs { - data, err = DecodeHookExec(f1, newFrom, t) + for _, c := range cached { + data, err = c(newFrom, t) if err != nil { return nil, err } @@ -81,13 +109,17 @@ func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc { // OrComposeDecodeHookFunc executes all input hook functions until one of them returns no error. In that case its value is returned. // If all hooks return an error, OrComposeDecodeHookFunc returns an error concatenating all error messages. func OrComposeDecodeHookFunc(ff ...DecodeHookFunc) DecodeHookFunc { + cached := make([]func(from reflect.Value, to reflect.Value) (interface{}, error), 0, len(ff)) + for _, f := range ff { + cached = append(cached, cachedDecodeHook(f)) + } return func(a, b reflect.Value) (interface{}, error) { var allErrs string var out interface{} var err error - for _, f := range ff { - out, err = DecodeHookExec(f, a, b) + for _, c := range cached { + out, err = c(a, b) if err != nil { allErrs += err.Error() + "\n" continue diff --git a/tools/vendor/github.com/go-viper/mapstructure/v2/mapstructure.go b/tools/vendor/github.com/go-viper/mapstructure/v2/mapstructure.go index 4b54fae087..1cd6204bb0 100644 --- a/tools/vendor/github.com/go-viper/mapstructure/v2/mapstructure.go +++ b/tools/vendor/github.com/go-viper/mapstructure/v2/mapstructure.go @@ -266,6 +266,10 @@ type DecoderConfig struct { // defaults to "mapstructure" TagName string + // The option of the value in the tag that indicates a field should + // be squashed. This defaults to "squash". + SquashTagOption string + // IgnoreUntaggedFields ignores all struct fields without explicit // TagName, comparable to `mapstructure:"-"` as default behaviour. IgnoreUntaggedFields bool @@ -283,7 +287,8 @@ type DecoderConfig struct { // structure. The top-level Decode method is just a convenience that sets // up the most basic Decoder. type Decoder struct { - config *DecoderConfig + config *DecoderConfig + cachedDecodeHook func(from reflect.Value, to reflect.Value) (interface{}, error) } // Metadata contains information about decoding a structure that @@ -401,6 +406,10 @@ func NewDecoder(config *DecoderConfig) (*Decoder, error) { config.TagName = "mapstructure" } + if config.SquashTagOption == "" { + config.SquashTagOption = "squash" + } + if config.MatchName == nil { config.MatchName = strings.EqualFold } @@ -408,6 +417,9 @@ func NewDecoder(config *DecoderConfig) (*Decoder, error) { result := &Decoder{ config: config, } + if config.DecodeHook != nil { + result.cachedDecodeHook = cachedDecodeHook(config.DecodeHook) + } return result, nil } @@ -462,10 +474,10 @@ func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) e return nil } - if d.config.DecodeHook != nil { + if d.cachedDecodeHook != nil { // We have a DecodeHook, so let's pre-process the input. var err error - input, err = DecodeHookExec(d.config.DecodeHook, inputVal, outVal) + input, err = d.cachedDecodeHook(inputVal, outVal) if err != nil { return fmt.Errorf("error decoding '%s': %w", name, err) } @@ -973,7 +985,7 @@ func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val re } // If "squash" is specified in the tag, we squash the field down. - squash = squash || strings.Index(tagValue[index+1:], "squash") != -1 + squash = squash || strings.Contains(tagValue[index+1:], d.config.SquashTagOption) if squash { // When squashing, the embedded type can be a pointer to a struct. if v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct { @@ -1351,7 +1363,7 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e // We always parse the tags cause we're looking for other tags too tagParts := strings.Split(fieldType.Tag.Get(d.config.TagName), ",") for _, tag := range tagParts[1:] { - if tag == "squash" { + if tag == d.config.SquashTagOption { squash = true break } @@ -1363,10 +1375,15 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e } if squash { - if fieldVal.Kind() != reflect.Struct { - errs = append(errs, fmt.Errorf("%s: unsupported type for squash: %s", fieldType.Name, fieldVal.Kind())) - } else { + switch fieldVal.Kind() { + case reflect.Struct: structs = append(structs, fieldVal) + case reflect.Interface: + if !fieldVal.IsNil() { + structs = append(structs, fieldVal.Elem().Elem()) + } + default: + errs = append(errs, fmt.Errorf("%s: unsupported type for squash: %s", fieldType.Name, fieldVal.Kind())) } continue } diff --git a/tools/vendor/github.com/gofrs/flock/.golangci.yml b/tools/vendor/github.com/gofrs/flock/.golangci.yml new file mode 100644 index 0000000000..3ad88a38fc --- /dev/null +++ b/tools/vendor/github.com/gofrs/flock/.golangci.yml @@ -0,0 +1,114 @@ +run: + timeout: 10m + +linters: + enable: + - asasalint + - bidichk + - dogsled + - dupword + - durationcheck + - err113 + - errname + - errorlint + - fatcontext + - forbidigo + - gocheckcompilerdirectives + - gochecknoinits + - gocritic + - godot + - godox + - gofumpt + - goheader + - goimports + - gomoddirectives + - goprintffuncname + - gosec + - inamedparam + - interfacebloat + - ireturn + - mirror + - misspell + - nolintlint + - revive + - stylecheck + - tenv + - testifylint + - thelper + - unconvert + - unparam + - usestdlibvars + - whitespace + +linters-settings: + misspell: + locale: US + godox: + keywords: + - FIXME + goheader: + template: |- + Copyright 2015 Tim Heckman. All rights reserved. + Copyright 2018-{{ YEAR }} The Gofrs. All rights reserved. + Use of this source code is governed by the BSD 3-Clause + license that can be found in the LICENSE file. + gofumpt: + extra-rules: true + gocritic: + enabled-tags: + - diagnostic + - style + - performance + disabled-checks: + - paramTypeCombine # already handle by gofumpt.extra-rules + - whyNoLint # already handle by nonolint + - unnamedResult + - hugeParam + - sloppyReassign + - rangeValCopy + - octalLiteral + - ptrToRefParam + - appendAssign + - ruleguard + - httpNoBody + - exposedSyncMutex + + revive: + rules: + - name: struct-tag + - name: blank-imports + - name: context-as-argument + - name: context-keys-type + - name: dot-imports + - name: error-return + - name: error-strings + - name: error-naming + - name: exported + - name: if-return + - name: increment-decrement + - name: var-naming + - name: var-declaration + - name: package-comments + - name: range + - name: receiver-naming + - name: time-naming + - name: unexported-return + - name: indent-error-flow + - name: errorf + - name: empty-block + - name: superfluous-else + - name: unused-parameter + - name: unreachable-code + - name: redefines-builtin-id + +issues: + exclude-use-default: true + max-issues-per-linter: 0 + max-same-issues: 0 + +output: + show-stats: true + sort-results: true + sort-order: + - linter + - file diff --git a/tools/vendor/github.com/gofrs/flock/.travis.yml b/tools/vendor/github.com/gofrs/flock/.travis.yml deleted file mode 100644 index b16d040fa8..0000000000 --- a/tools/vendor/github.com/gofrs/flock/.travis.yml +++ /dev/null @@ -1,10 +0,0 @@ -language: go -go: - - 1.14.x - - 1.15.x -script: go test -v -check.vv -race ./... -sudo: false -notifications: - email: - on_success: never - on_failure: always diff --git a/tools/vendor/github.com/gofrs/flock/LICENSE b/tools/vendor/github.com/gofrs/flock/LICENSE index 8b8ff36fe4..7de525bf02 100644 --- a/tools/vendor/github.com/gofrs/flock/LICENSE +++ b/tools/vendor/github.com/gofrs/flock/LICENSE @@ -1,3 +1,4 @@ +Copyright (c) 2018-2024, The Gofrs Copyright (c) 2015-2020, Tim Heckman All rights reserved. diff --git a/tools/vendor/github.com/gofrs/flock/Makefile b/tools/vendor/github.com/gofrs/flock/Makefile new file mode 100644 index 0000000000..65c139d68c --- /dev/null +++ b/tools/vendor/github.com/gofrs/flock/Makefile @@ -0,0 +1,15 @@ +.PHONY: lint test test_race build_cross_os + +default: lint test build_cross_os + +test: + go test -v -cover ./... + +test_race: + CGO_ENABLED=1 go test -v -race ./... + +lint: + golangci-lint run + +build_cross_os: + ./build.sh diff --git a/tools/vendor/github.com/gofrs/flock/README.md b/tools/vendor/github.com/gofrs/flock/README.md index 71ce63692e..f7ca0dd9c2 100644 --- a/tools/vendor/github.com/gofrs/flock/README.md +++ b/tools/vendor/github.com/gofrs/flock/README.md @@ -1,26 +1,22 @@ # flock -[![TravisCI Build Status](https://img.shields.io/travis/gofrs/flock/master.svg?style=flat)](https://travis-ci.org/gofrs/flock) -[![GoDoc](https://img.shields.io/badge/godoc-flock-blue.svg?style=flat)](https://godoc.org/github.com/gofrs/flock) -[![License](https://img.shields.io/badge/license-BSD_3--Clause-brightgreen.svg?style=flat)](https://github.com/gofrs/flock/blob/master/LICENSE) -[![Go Report Card](https://goreportcard.com/badge/github.com/gofrs/flock)](https://goreportcard.com/report/github.com/gofrs/flock) -`flock` implements a thread-safe sync.Locker interface for file locking. It also -includes a non-blocking TryLock() function to allow locking without blocking execution. +[![Go Reference](https://pkg.go.dev/badge/github.com/gofrs/flock.svg)](https://pkg.go.dev/github.com/gofrs/flock) +[![License](https://img.shields.io/badge/license-BSD_3--Clause-brightgreen.svg?style=flat)](https://github.com/gofrs/flock/blob/main/LICENSE) +[![Go Report Card](https://goreportcard.com/badge/github.com/gofrs/flock)](https://goreportcard.com/report/github.com/gofrs/flock) -## License -`flock` is released under the BSD 3-Clause License. See the `LICENSE` file for more details. +`flock` implements a thread-safe file lock. -## Go Compatibility -This package makes use of the `context` package that was introduced in Go 1.7. As such, this -package has an implicit dependency on Go 1.7+. +It also includes a non-blocking `TryLock()` function to allow locking without blocking execution. ## Installation -``` + +```bash go get -u github.com/gofrs/flock ``` ## Usage -```Go + +```go import "github.com/gofrs/flock" fileLock := flock.New("/var/lock/go-lock.lock") @@ -38,4 +34,12 @@ if locked { ``` For more detailed usage information take a look at the package API docs on -[GoDoc](https://godoc.org/github.com/gofrs/flock). +[GoDoc](https://pkg.go.dev/github.com/gofrs/flock). + +## License + +`flock` is released under the BSD 3-Clause License. See the [`LICENSE`](./LICENSE) file for more details. + +## Project History + +This project was originally `github.com/theckman/go-flock`, it was transferred to Gofrs by the original author [Tim Heckman ](https://github.com/theckman). diff --git a/tools/vendor/github.com/gofrs/flock/SECURITY.md b/tools/vendor/github.com/gofrs/flock/SECURITY.md new file mode 100644 index 0000000000..01419bd592 --- /dev/null +++ b/tools/vendor/github.com/gofrs/flock/SECURITY.md @@ -0,0 +1,21 @@ +# Security Policy + +## Supported Versions + +We support the latest version of this library. +We do not guarantee support of previous versions. + +If a defect is reported, it will generally be fixed on the latest version (provided it exists) irrespective of whether it was introduced in a prior version. + +## Reporting a Vulnerability + +To report a potential security vulnerability, please create a [security advisory](https://github.com/gofrs/flock/security/advisories/new). + +For us to respond to your report most effectively, please include any of the following: + +- Steps to reproduce or a proof-of-concept +- Any relevant information, including the versions used + +## Security Scorecard + +This project submits security [results](https://scorecard.dev/viewer/?uri=github.com/gofrs/flock) to the [OpenSSF Scorecard](https://securityscorecards.dev/). diff --git a/tools/vendor/github.com/gofrs/flock/appveyor.yml b/tools/vendor/github.com/gofrs/flock/appveyor.yml deleted file mode 100644 index 909b4bf7cb..0000000000 --- a/tools/vendor/github.com/gofrs/flock/appveyor.yml +++ /dev/null @@ -1,25 +0,0 @@ -version: '{build}' - -build: false -deploy: false - -clone_folder: 'c:\gopath\src\github.com\gofrs\flock' - -environment: - GOPATH: 'c:\gopath' - GOVERSION: '1.15' - -init: - - git config --global core.autocrlf input - -install: - - rmdir c:\go /s /q - - appveyor DownloadFile https://storage.googleapis.com/golang/go%GOVERSION%.windows-amd64.msi - - msiexec /i go%GOVERSION%.windows-amd64.msi /q - - set Path=c:\go\bin;c:\gopath\bin;%Path% - - go version - - go env - -test_script: - - go get -t ./... - - go test -race -v ./... diff --git a/tools/vendor/github.com/gofrs/flock/build.sh b/tools/vendor/github.com/gofrs/flock/build.sh new file mode 100644 index 0000000000..60f7809f06 --- /dev/null +++ b/tools/vendor/github.com/gofrs/flock/build.sh @@ -0,0 +1,18 @@ +#!/bin/bash -e + +# Not supported by flock: +# - plan9/* +# - js/wasm +# - wasp1/wasm + +for row in $(go tool dist list -json | jq -r '.[] | @base64'); do + _jq() { + echo ${row} | base64 --decode | jq -r ${1} + } + + GOOS=$(_jq '.GOOS') + GOARCH=$(_jq '.GOARCH') + + echo "$GOOS/$GOARCH" + GOOS=$GOOS GOARCH=$GOARCH go build +done diff --git a/tools/vendor/github.com/gofrs/flock/flock.go b/tools/vendor/github.com/gofrs/flock/flock.go index 95c784ca50..ff942b228a 100644 --- a/tools/vendor/github.com/gofrs/flock/flock.go +++ b/tools/vendor/github.com/gofrs/flock/flock.go @@ -1,4 +1,5 @@ // Copyright 2015 Tim Heckman. All rights reserved. +// Copyright 2018-2024 The Gofrs. All rights reserved. // Use of this source code is governed by the BSD 3-Clause // license that can be found in the LICENSE file. @@ -18,12 +19,29 @@ package flock import ( "context" + "io/fs" "os" "runtime" "sync" "time" ) +type Option func(f *Flock) + +// SetFlag sets the flag used to create/open the file. +func SetFlag(flag int) Option { + return func(f *Flock) { + f.flag = flag + } +} + +// SetPermissions sets the OS permissions to set on the file. +func SetPermissions(perm fs.FileMode) Option { + return func(f *Flock) { + f.perm = perm + } +} + // Flock is the struct type to handle file locking. All fields are unexported, // with access to some of the fields provided by getter methods (Path() and Locked()). type Flock struct { @@ -32,12 +50,37 @@ type Flock struct { fh *os.File l bool r bool + + // flag is the flag used to create/open the file. + flag int + // perm is the OS permissions to set on the file. + perm fs.FileMode } // New returns a new instance of *Flock. The only parameter // it takes is the path to the desired lockfile. -func New(path string) *Flock { - return &Flock{path: path} +func New(path string, opts ...Option) *Flock { + // create it if it doesn't exist, and open the file read-only. + flags := os.O_CREATE + switch runtime.GOOS { + case "aix", "solaris", "illumos": + // AIX cannot preform write-lock (i.e. exclusive) on a read-only file. + flags |= os.O_RDWR + default: + flags |= os.O_RDONLY + } + + f := &Flock{ + path: path, + flag: flags, + perm: fs.FileMode(0o600), + } + + for _, opt := range opts { + opt(f) + } + + return f } // NewFlock returns a new instance of *Flock. The only parameter @@ -67,6 +110,7 @@ func (f *Flock) Path() string { func (f *Flock) Locked() bool { f.m.RLock() defer f.m.RUnlock() + return f.l } @@ -76,6 +120,7 @@ func (f *Flock) Locked() bool { func (f *Flock) RLocked() bool { f.m.RLock() defer f.m.RUnlock() + return f.r } @@ -83,16 +128,18 @@ func (f *Flock) String() string { return f.path } -// TryLockContext repeatedly tries to take an exclusive lock until one of the -// conditions is met: TryLock succeeds, TryLock fails with error, or Context -// Done channel is closed. +// TryLockContext repeatedly tries to take an exclusive lock until one of the conditions is met: +// - TryLock succeeds +// - TryLock fails with error +// - Context Done channel is closed. func (f *Flock) TryLockContext(ctx context.Context, retryDelay time.Duration) (bool, error) { return tryCtx(ctx, f.TryLock, retryDelay) } -// TryRLockContext repeatedly tries to take a shared lock until one of the -// conditions is met: TryRLock succeeds, TryRLock fails with error, or Context -// Done channel is closed. +// TryRLockContext repeatedly tries to take a shared lock until one of the conditions is met: +// - TryRLock succeeds +// - TryRLock fails with error +// - Context Done channel is closed. func (f *Flock) TryRLockContext(ctx context.Context, retryDelay time.Duration) (bool, error) { return tryCtx(ctx, f.TryRLock, retryDelay) } @@ -101,10 +148,12 @@ func tryCtx(ctx context.Context, fn func() (bool, error), retryDelay time.Durati if ctx.Err() != nil { return false, ctx.Err() } + for { if ok, err := fn(); ok || err != nil { return ok, err } + select { case <-ctx.Done(): return false, ctx.Err() @@ -114,31 +163,44 @@ func tryCtx(ctx context.Context, fn func() (bool, error), retryDelay time.Durati } } -func (f *Flock) setFh() error { +func (f *Flock) setFh(flag int) error { // open a new os.File instance - // create it if it doesn't exist, and open the file read-only. - flags := os.O_CREATE - if runtime.GOOS == "aix" { - // AIX cannot preform write-lock (ie exclusive) on a - // read-only file. - flags |= os.O_RDWR - } else { - flags |= os.O_RDONLY - } - fh, err := os.OpenFile(f.path, flags, os.FileMode(0600)) + fh, err := os.OpenFile(f.path, flag, f.perm) if err != nil { return err } - // set the filehandle on the struct + // set the file handle on the struct f.fh = fh + return nil } -// ensure the file handle is closed if no lock is held +// resetFh resets file handle: +// - tries to close the file (ignore errors) +// - sets fh to nil. +func (f *Flock) resetFh() { + if f.fh == nil { + return + } + + _ = f.fh.Close() + + f.fh = nil +} + +// ensure the file handle is closed if no lock is held. func (f *Flock) ensureFhState() { - if !f.l && !f.r && f.fh != nil { - f.fh.Close() - f.fh = nil + if f.l || f.r || f.fh == nil { + return } + + f.resetFh() +} + +func (f *Flock) reset() { + f.l = false + f.r = false + + f.resetFh() } diff --git a/tools/vendor/github.com/gofrs/flock/flock_aix.go b/tools/vendor/github.com/gofrs/flock/flock_aix.go deleted file mode 100644 index 7277c1b6b2..0000000000 --- a/tools/vendor/github.com/gofrs/flock/flock_aix.go +++ /dev/null @@ -1,281 +0,0 @@ -// Copyright 2019 Tim Heckman. All rights reserved. Use of this source code is -// governed by the BSD 3-Clause license that can be found in the LICENSE file. - -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This code implements the filelock API using POSIX 'fcntl' locks, which attach -// to an (inode, process) pair rather than a file descriptor. To avoid unlocking -// files prematurely when the same file is opened through different descriptors, -// we allow only one read-lock at a time. -// -// This code is adapted from the Go package: -// cmd/go/internal/lockedfile/internal/filelock - -//+build aix - -package flock - -import ( - "errors" - "io" - "os" - "sync" - "syscall" - - "golang.org/x/sys/unix" -) - -type lockType int16 - -const ( - readLock lockType = unix.F_RDLCK - writeLock lockType = unix.F_WRLCK -) - -type cmdType int - -const ( - tryLock cmdType = unix.F_SETLK - waitLock cmdType = unix.F_SETLKW -) - -type inode = uint64 - -type inodeLock struct { - owner *Flock - queue []<-chan *Flock -} - -var ( - mu sync.Mutex - inodes = map[*Flock]inode{} - locks = map[inode]inodeLock{} -) - -// Lock is a blocking call to try and take an exclusive file lock. It will wait -// until it is able to obtain the exclusive file lock. It's recommended that -// TryLock() be used over this function. This function may block the ability to -// query the current Locked() or RLocked() status due to a RW-mutex lock. -// -// If we are already exclusive-locked, this function short-circuits and returns -// immediately assuming it can take the mutex lock. -// -// If the *Flock has a shared lock (RLock), this may transparently replace the -// shared lock with an exclusive lock on some UNIX-like operating systems. Be -// careful when using exclusive locks in conjunction with shared locks -// (RLock()), because calling Unlock() may accidentally release the exclusive -// lock that was once a shared lock. -func (f *Flock) Lock() error { - return f.lock(&f.l, writeLock) -} - -// RLock is a blocking call to try and take a shared file lock. It will wait -// until it is able to obtain the shared file lock. It's recommended that -// TryRLock() be used over this function. This function may block the ability to -// query the current Locked() or RLocked() status due to a RW-mutex lock. -// -// If we are already shared-locked, this function short-circuits and returns -// immediately assuming it can take the mutex lock. -func (f *Flock) RLock() error { - return f.lock(&f.r, readLock) -} - -func (f *Flock) lock(locked *bool, flag lockType) error { - f.m.Lock() - defer f.m.Unlock() - - if *locked { - return nil - } - - if f.fh == nil { - if err := f.setFh(); err != nil { - return err - } - defer f.ensureFhState() - } - - if _, err := f.doLock(waitLock, flag, true); err != nil { - return err - } - - *locked = true - return nil -} - -func (f *Flock) doLock(cmd cmdType, lt lockType, blocking bool) (bool, error) { - // POSIX locks apply per inode and process, and the lock for an inode is - // released when *any* descriptor for that inode is closed. So we need to - // synchronize access to each inode internally, and must serialize lock and - // unlock calls that refer to the same inode through different descriptors. - fi, err := f.fh.Stat() - if err != nil { - return false, err - } - ino := inode(fi.Sys().(*syscall.Stat_t).Ino) - - mu.Lock() - if i, dup := inodes[f]; dup && i != ino { - mu.Unlock() - return false, &os.PathError{ - Path: f.Path(), - Err: errors.New("inode for file changed since last Lock or RLock"), - } - } - - inodes[f] = ino - - var wait chan *Flock - l := locks[ino] - if l.owner == f { - // This file already owns the lock, but the call may change its lock type. - } else if l.owner == nil { - // No owner: it's ours now. - l.owner = f - } else if !blocking { - // Already owned: cannot take the lock. - mu.Unlock() - return false, nil - } else { - // Already owned: add a channel to wait on. - wait = make(chan *Flock) - l.queue = append(l.queue, wait) - } - locks[ino] = l - mu.Unlock() - - if wait != nil { - wait <- f - } - - err = setlkw(f.fh.Fd(), cmd, lt) - - if err != nil { - f.doUnlock() - if cmd == tryLock && err == unix.EACCES { - return false, nil - } - return false, err - } - - return true, nil -} - -func (f *Flock) Unlock() error { - f.m.Lock() - defer f.m.Unlock() - - // if we aren't locked or if the lockfile instance is nil - // just return a nil error because we are unlocked - if (!f.l && !f.r) || f.fh == nil { - return nil - } - - if err := f.doUnlock(); err != nil { - return err - } - - f.fh.Close() - - f.l = false - f.r = false - f.fh = nil - - return nil -} - -func (f *Flock) doUnlock() (err error) { - var owner *Flock - mu.Lock() - ino, ok := inodes[f] - if ok { - owner = locks[ino].owner - } - mu.Unlock() - - if owner == f { - err = setlkw(f.fh.Fd(), waitLock, unix.F_UNLCK) - } - - mu.Lock() - l := locks[ino] - if len(l.queue) == 0 { - // No waiters: remove the map entry. - delete(locks, ino) - } else { - // The first waiter is sending us their file now. - // Receive it and update the queue. - l.owner = <-l.queue[0] - l.queue = l.queue[1:] - locks[ino] = l - } - delete(inodes, f) - mu.Unlock() - - return err -} - -// TryLock is the preferred function for taking an exclusive file lock. This -// function takes an RW-mutex lock before it tries to lock the file, so there is -// the possibility that this function may block for a short time if another -// goroutine is trying to take any action. -// -// The actual file lock is non-blocking. If we are unable to get the exclusive -// file lock, the function will return false instead of waiting for the lock. If -// we get the lock, we also set the *Flock instance as being exclusive-locked. -func (f *Flock) TryLock() (bool, error) { - return f.try(&f.l, writeLock) -} - -// TryRLock is the preferred function for taking a shared file lock. This -// function takes an RW-mutex lock before it tries to lock the file, so there is -// the possibility that this function may block for a short time if another -// goroutine is trying to take any action. -// -// The actual file lock is non-blocking. If we are unable to get the shared file -// lock, the function will return false instead of waiting for the lock. If we -// get the lock, we also set the *Flock instance as being share-locked. -func (f *Flock) TryRLock() (bool, error) { - return f.try(&f.r, readLock) -} - -func (f *Flock) try(locked *bool, flag lockType) (bool, error) { - f.m.Lock() - defer f.m.Unlock() - - if *locked { - return true, nil - } - - if f.fh == nil { - if err := f.setFh(); err != nil { - return false, err - } - defer f.ensureFhState() - } - - haslock, err := f.doLock(tryLock, flag, false) - if err != nil { - return false, err - } - - *locked = haslock - return haslock, nil -} - -// setlkw calls FcntlFlock with cmd for the entire file indicated by fd. -func setlkw(fd uintptr, cmd cmdType, lt lockType) error { - for { - err := unix.FcntlFlock(fd, int(cmd), &unix.Flock_t{ - Type: int16(lt), - Whence: io.SeekStart, - Start: 0, - Len: 0, // All bytes. - }) - if err != unix.EINTR { - return err - } - } -} diff --git a/tools/vendor/github.com/gofrs/flock/flock_others.go b/tools/vendor/github.com/gofrs/flock/flock_others.go new file mode 100644 index 0000000000..18b14f1bd7 --- /dev/null +++ b/tools/vendor/github.com/gofrs/flock/flock_others.go @@ -0,0 +1,40 @@ +//go:build (!unix && !windows) || plan9 + +package flock + +import ( + "errors" + "io/fs" +) + +func (f *Flock) Lock() error { + return &fs.PathError{ + Op: "Lock", + Path: f.Path(), + Err: errors.ErrUnsupported, + } +} + +func (f *Flock) RLock() error { + return &fs.PathError{ + Op: "RLock", + Path: f.Path(), + Err: errors.ErrUnsupported, + } +} + +func (f *Flock) Unlock() error { + return &fs.PathError{ + Op: "Unlock", + Path: f.Path(), + Err: errors.ErrUnsupported, + } +} + +func (f *Flock) TryLock() (bool, error) { + return false, f.Lock() +} + +func (f *Flock) TryRLock() (bool, error) { + return false, f.RLock() +} diff --git a/tools/vendor/github.com/gofrs/flock/flock_unix.go b/tools/vendor/github.com/gofrs/flock/flock_unix.go index c315a3e290..cf8919c7ad 100644 --- a/tools/vendor/github.com/gofrs/flock/flock_unix.go +++ b/tools/vendor/github.com/gofrs/flock/flock_unix.go @@ -1,42 +1,44 @@ // Copyright 2015 Tim Heckman. All rights reserved. +// Copyright 2018-2024 The Gofrs. All rights reserved. // Use of this source code is governed by the BSD 3-Clause // license that can be found in the LICENSE file. -// +build !aix,!windows +//go:build darwin || dragonfly || freebsd || illumos || linux || netbsd || openbsd package flock import ( + "errors" "os" - "syscall" + + "golang.org/x/sys/unix" ) -// Lock is a blocking call to try and take an exclusive file lock. It will wait -// until it is able to obtain the exclusive file lock. It's recommended that -// TryLock() be used over this function. This function may block the ability to -// query the current Locked() or RLocked() status due to a RW-mutex lock. +// Lock is a blocking call to try and take an exclusive file lock. +// It will wait until it is able to obtain the exclusive file lock. +// It's recommended that TryLock() be used over this function. +// This function may block the ability to query the current Locked() or RLocked() status due to a RW-mutex lock. // -// If we are already exclusive-locked, this function short-circuits and returns -// immediately assuming it can take the mutex lock. +// If we are already exclusive-locked, +// this function short-circuits and returns immediately assuming it can take the mutex lock. // -// If the *Flock has a shared lock (RLock), this may transparently replace the -// shared lock with an exclusive lock on some UNIX-like operating systems. Be -// careful when using exclusive locks in conjunction with shared locks -// (RLock()), because calling Unlock() may accidentally release the exclusive -// lock that was once a shared lock. +// If the *Flock has a shared lock (RLock), +// this may transparently replace the shared lock with an exclusive lock on some UNIX-like operating systems. +// Be careful when using exclusive locks in conjunction with shared locks (RLock()), +// because calling Unlock() may accidentally release the exclusive lock that was once a shared lock. func (f *Flock) Lock() error { - return f.lock(&f.l, syscall.LOCK_EX) + return f.lock(&f.l, unix.LOCK_EX) } -// RLock is a blocking call to try and take a shared file lock. It will wait -// until it is able to obtain the shared file lock. It's recommended that -// TryRLock() be used over this function. This function may block the ability to -// query the current Locked() or RLocked() status due to a RW-mutex lock. +// RLock is a blocking call to try and take a shared file lock. +// It will wait until it is able to obtain the shared file lock. +// It's recommended that TryRLock() be used over this function. +// This function may block the ability to query the current Locked() or RLocked() status due to a RW-mutex lock. // -// If we are already shared-locked, this function short-circuits and returns -// immediately assuming it can take the mutex lock. +// If we are already shared-locked, +// this function short-circuits and returns immediately assuming it can take the mutex lock. func (f *Flock) RLock() error { - return f.lock(&f.r, syscall.LOCK_SH) + return f.lock(&f.r, unix.LOCK_SH) } func (f *Flock) lock(locked *bool, flag int) error { @@ -48,13 +50,15 @@ func (f *Flock) lock(locked *bool, flag int) error { } if f.fh == nil { - if err := f.setFh(); err != nil { + if err := f.setFh(f.flag); err != nil { return err } + defer f.ensureFhState() } - if err := syscall.Flock(int(f.fh.Fd()), flag); err != nil { + err := unix.Flock(int(f.fh.Fd()), flag) + if err != nil { shouldRetry, reopenErr := f.reopenFDOnError(err) if reopenErr != nil { return reopenErr @@ -64,71 +68,74 @@ func (f *Flock) lock(locked *bool, flag int) error { return err } - if err = syscall.Flock(int(f.fh.Fd()), flag); err != nil { + err = unix.Flock(int(f.fh.Fd()), flag) + if err != nil { return err } } *locked = true + return nil } -// Unlock is a function to unlock the file. This file takes a RW-mutex lock, so -// while it is running the Locked() and RLocked() functions will be blocked. +// Unlock is a function to unlock the file. +// This file takes a RW-mutex lock, +// so while it is running the Locked() and RLocked() functions will be blocked. // -// This function short-circuits if we are unlocked already. If not, it calls -// syscall.LOCK_UN on the file and closes the file descriptor. It does not -// remove the file from disk. It's up to your application to do. +// This function short-circuits if we are unlocked already. +// If not, it calls unix.LOCK_UN on the file and closes the file descriptor. +// It does not remove the file from disk. It's up to your application to do. // -// Please note, if your shared lock became an exclusive lock this may -// unintentionally drop the exclusive lock if called by the consumer that -// believes they have a shared lock. Please see Lock() for more details. +// Please note, +// if your shared lock became an exclusive lock, +// this may unintentionally drop the exclusive lock if called by the consumer that believes they have a shared lock. +// Please see Lock() for more details. func (f *Flock) Unlock() error { f.m.Lock() defer f.m.Unlock() - // if we aren't locked or if the lockfile instance is nil - // just return a nil error because we are unlocked + // If we aren't locked or if the lockfile instance is nil + // just return a nil error because we are unlocked. if (!f.l && !f.r) || f.fh == nil { return nil } - // mark the file as unlocked - if err := syscall.Flock(int(f.fh.Fd()), syscall.LOCK_UN); err != nil { + // Mark the file as unlocked. + err := unix.Flock(int(f.fh.Fd()), unix.LOCK_UN) + if err != nil { return err } - f.fh.Close() - - f.l = false - f.r = false - f.fh = nil + f.reset() return nil } -// TryLock is the preferred function for taking an exclusive file lock. This -// function takes an RW-mutex lock before it tries to lock the file, so there is -// the possibility that this function may block for a short time if another -// goroutine is trying to take any action. +// TryLock is the preferred function for taking an exclusive file lock. +// This function takes an RW-mutex lock before it tries to lock the file, +// so there is the possibility that this function may block for a short time +// if another goroutine is trying to take any action. // -// The actual file lock is non-blocking. If we are unable to get the exclusive -// file lock, the function will return false instead of waiting for the lock. If -// we get the lock, we also set the *Flock instance as being exclusive-locked. +// The actual file lock is non-blocking. +// If we are unable to get the exclusive file lock, +// the function will return false instead of waiting for the lock. +// If we get the lock, we also set the *Flock instance as being exclusive-locked. func (f *Flock) TryLock() (bool, error) { - return f.try(&f.l, syscall.LOCK_EX) + return f.try(&f.l, unix.LOCK_EX) } -// TryRLock is the preferred function for taking a shared file lock. This -// function takes an RW-mutex lock before it tries to lock the file, so there is -// the possibility that this function may block for a short time if another -// goroutine is trying to take any action. +// TryRLock is the preferred function for taking a shared file lock. +// This function takes an RW-mutex lock before it tries to lock the file, +// so there is the possibility that this function may block for a short time +// if another goroutine is trying to take any action. // -// The actual file lock is non-blocking. If we are unable to get the shared file -// lock, the function will return false instead of waiting for the lock. If we -// get the lock, we also set the *Flock instance as being share-locked. +// The actual file lock is non-blocking. +// If we are unable to get the shared file lock, +// the function will return false instead of waiting for the lock. +// If we get the lock, we also set the *Flock instance as being share-locked. func (f *Flock) TryRLock() (bool, error) { - return f.try(&f.r, syscall.LOCK_SH) + return f.try(&f.r, unix.LOCK_SH) } func (f *Flock) try(locked *bool, flag int) (bool, error) { @@ -140,25 +147,28 @@ func (f *Flock) try(locked *bool, flag int) (bool, error) { } if f.fh == nil { - if err := f.setFh(); err != nil { + if err := f.setFh(f.flag); err != nil { return false, err } + defer f.ensureFhState() } var retried bool retry: - err := syscall.Flock(int(f.fh.Fd()), flag|syscall.LOCK_NB) + err := unix.Flock(int(f.fh.Fd()), flag|unix.LOCK_NB) - switch err { - case syscall.EWOULDBLOCK: + switch { + case errors.Is(err, unix.EWOULDBLOCK): return false, nil - case nil: + case err == nil: *locked = true return true, nil } + if !retried { - if shouldRetry, reopenErr := f.reopenFDOnError(err); reopenErr != nil { + shouldRetry, reopenErr := f.reopenFDOnError(err) + if reopenErr != nil { return false, reopenErr } else if shouldRetry { retried = true @@ -169,29 +179,32 @@ retry: return false, err } -// reopenFDOnError determines whether we should reopen the file handle -// in readwrite mode and try again. This comes from util-linux/sys-utils/flock.c: -// Since Linux 3.4 (commit 55725513) -// Probably NFSv4 where flock() is emulated by fcntl(). +// reopenFDOnError determines whether we should reopen the file handle in readwrite mode and try again. +// This comes from `util-linux/sys-utils/flock.c`: +// > Since Linux 3.4 (commit 55725513) +// > Probably NFSv4 where flock() is emulated by fcntl(). +// > https://github.com/util-linux/util-linux/blob/198e920aa24743ef6ace4e07cf6237de527f9261/sys-utils/flock.c#L374-L390 func (f *Flock) reopenFDOnError(err error) (bool, error) { - if err != syscall.EIO && err != syscall.EBADF { + if !errors.Is(err, unix.EIO) && !errors.Is(err, unix.EBADF) { return false, nil } - if st, err := f.fh.Stat(); err == nil { - // if the file is able to be read and written - if st.Mode()&0600 == 0600 { - f.fh.Close() - f.fh = nil - - // reopen in read-write mode and set the filehandle - fh, err := os.OpenFile(f.path, os.O_CREATE|os.O_RDWR, os.FileMode(0600)) - if err != nil { - return false, err - } - f.fh = fh - return true, nil - } + + st, err := f.fh.Stat() + if err != nil { + return false, nil + } + + if st.Mode()&f.perm != f.perm { + return false, nil + } + + f.resetFh() + + // reopen in read-write mode and set the file handle + err = f.setFh(f.flag | os.O_RDWR) + if err != nil { + return false, err } - return false, nil + return true, nil } diff --git a/tools/vendor/github.com/gofrs/flock/flock_unix_fcntl.go b/tools/vendor/github.com/gofrs/flock/flock_unix_fcntl.go new file mode 100644 index 0000000000..ea007b47d9 --- /dev/null +++ b/tools/vendor/github.com/gofrs/flock/flock_unix_fcntl.go @@ -0,0 +1,393 @@ +// Copyright 2015 Tim Heckman. All rights reserved. +// Copyright 2018-2024 The Gofrs. All rights reserved. +// Use of this source code is governed by the BSD 3-Clause +// license that can be found in the LICENSE file. + +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This code implements the filelock API using POSIX 'fcntl' locks, +// which attach to an (inode, process) pair rather than a file descriptor. +// To avoid unlocking files prematurely when the same file is opened through different descriptors, +// we allow only one read-lock at a time. +// +// This code is adapted from the Go package (go.22): +// https://github.com/golang/go/blob/release-branch.go1.22/src/cmd/go/internal/lockedfile/internal/filelock/filelock_fcntl.go + +//go:build aix || (solaris && !illumos) + +package flock + +import ( + "errors" + "io" + "io/fs" + "math/rand" + "sync" + "syscall" + "time" + + "golang.org/x/sys/unix" +) + +// https://github.com/golang/go/blob/09aeb6e33ab426eff4676a3baf694d5a3019e9fc/src/cmd/go/internal/lockedfile/internal/filelock/filelock_fcntl.go#L28 +type lockType int16 + +// String returns the name of the function corresponding to lt +// (Lock, RLock, or Unlock). +// https://github.com/golang/go/blob/09aeb6e33ab426eff4676a3baf694d5a3019e9fc/src/cmd/go/internal/lockedfile/internal/filelock/filelock.go#L67 +func (lt lockType) String() string { + switch lt { + case readLock: + return "RLock" + case writeLock: + return "Lock" + default: + return "Unlock" + } +} + +// https://github.com/golang/go/blob/09aeb6e33ab426eff4676a3baf694d5a3019e9fc/src/cmd/go/internal/lockedfile/internal/filelock/filelock_fcntl.go#L30-L33 +const ( + readLock lockType = unix.F_RDLCK + writeLock lockType = unix.F_WRLCK +) + +// https://github.com/golang/go/blob/09aeb6e33ab426eff4676a3baf694d5a3019e9fc/src/cmd/go/internal/lockedfile/internal/filelock/filelock_fcntl.go#L35 +type inode = uint64 + +// https://github.com/golang/go/blob/09aeb6e33ab426eff4676a3baf694d5a3019e9fc/src/cmd/go/internal/lockedfile/internal/filelock/filelock_fcntl.go#L37-L40 +type inodeLock struct { + owner *Flock + queue []<-chan *Flock +} + +type cmdType int + +const ( + tryLock cmdType = unix.F_SETLK + waitLock cmdType = unix.F_SETLKW +) + +var ( + mu sync.Mutex + inodes = map[*Flock]inode{} + locks = map[inode]inodeLock{} +) + +// Lock is a blocking call to try and take an exclusive file lock. +// It will wait until it is able to obtain the exclusive file lock. +// It's recommended that TryLock() be used over this function. +// This function may block the ability to query the current Locked() or RLocked() status due to a RW-mutex lock. +// +// If we are already exclusive-locked, this function short-circuits and +// returns immediately assuming it can take the mutex lock. +// +// If the *Flock has a shared lock (RLock), +// this may transparently replace the shared lock with an exclusive lock on some UNIX-like operating systems. +// Be careful when using exclusive locks in conjunction with shared locks (RLock()), +// because calling Unlock() may accidentally release the exclusive lock that was once a shared lock. +func (f *Flock) Lock() error { + return f.lock(&f.l, writeLock) +} + +// RLock is a blocking call to try and take a shared file lock. +// It will wait until it is able to obtain the shared file lock. +// It's recommended that TryRLock() be used over this function. +// This function may block the ability to query the current Locked() or RLocked() status due to a RW-mutex lock. +// +// If we are already shared-locked, this function short-circuits and +// returns immediately assuming it can take the mutex lock. +func (f *Flock) RLock() error { + return f.lock(&f.r, readLock) +} + +func (f *Flock) lock(locked *bool, flag lockType) error { + f.m.Lock() + defer f.m.Unlock() + + if *locked { + return nil + } + + if f.fh == nil { + if err := f.setFh(f.flag); err != nil { + return err + } + + defer f.ensureFhState() + } + + _, err := f.doLock(waitLock, flag, true) + if err != nil { + return err + } + + *locked = true + + return nil +} + +// https://github.com/golang/go/blob/09aeb6e33ab426eff4676a3baf694d5a3019e9fc/src/cmd/go/internal/lockedfile/internal/filelock/filelock_fcntl.go#L48 +func (f *Flock) doLock(cmd cmdType, lt lockType, blocking bool) (bool, error) { + // POSIX locks apply per inode and process, + // and the lock for an inode is released when *any* descriptor for that inode is closed. + // So we need to synchronize access to each inode internally, + // and must serialize lock and unlock calls that refer to the same inode through different descriptors. + fi, err := f.fh.Stat() + if err != nil { + return false, err + } + + // Note(ldez): don't replace `syscall.Stat_t` by `unix.Stat_t` because `FileInfo.Sys()` returns `syscall.Stat_t` + ino := fi.Sys().(*syscall.Stat_t).Ino + + mu.Lock() + + if i, dup := inodes[f]; dup && i != ino { + mu.Unlock() + return false, &fs.PathError{ + Op: lt.String(), + Path: f.Path(), + Err: errors.New("inode for file changed since last Lock or RLock"), + } + } + + inodes[f] = ino + + var wait chan *Flock + + l := locks[ino] + + switch { + case l.owner == f: + // This file already owns the lock, but the call may change its lock type. + case l.owner == nil: + // No owner: it's ours now. + l.owner = f + + case !blocking: + // Already owned: cannot take the lock. + mu.Unlock() + return false, nil + + default: + // Already owned: add a channel to wait on. + wait = make(chan *Flock) + l.queue = append(l.queue, wait) + } + + locks[ino] = l + + mu.Unlock() + + if wait != nil { + wait <- f + } + + // Spurious EDEADLK errors arise on platforms that compute deadlock graphs at + // the process, rather than thread, level. Consider processes P and Q, with + // threads P.1, P.2, and Q.3. The following trace is NOT a deadlock, but will be + // reported as a deadlock on systems that consider only process granularity: + // + // P.1 locks file A. + // Q.3 locks file B. + // Q.3 blocks on file A. + // P.2 blocks on file B. (This is erroneously reported as a deadlock.) + // P.1 unlocks file A. + // Q.3 unblocks and locks file A. + // Q.3 unlocks files A and B. + // P.2 unblocks and locks file B. + // P.2 unlocks file B. + // + // These spurious errors were observed in practice on AIX and Solaris in + // cmd/go: see https://golang.org/issue/32817. + // + // We work around this bug by treating EDEADLK as always spurious. If there + // really is a lock-ordering bug between the interacting processes, it will + // become a livelock instead, but that's not appreciably worse than if we had + // a proper flock implementation (which generally does not even attempt to + // diagnose deadlocks). + // + // In the above example, that changes the trace to: + // + // P.1 locks file A. + // Q.3 locks file B. + // Q.3 blocks on file A. + // P.2 spuriously fails to lock file B and goes to sleep. + // P.1 unlocks file A. + // Q.3 unblocks and locks file A. + // Q.3 unlocks files A and B. + // P.2 wakes up and locks file B. + // P.2 unlocks file B. + // + // We know that the retry loop will not introduce a *spurious* livelock + // because, according to the POSIX specification, EDEADLK is only to be + // returned when “the lock is blocked by a lock from another process”. + // If that process is blocked on some lock that we are holding, then the + // resulting livelock is due to a real deadlock (and would manifest as such + // when using, for example, the flock implementation of this package). + // If the other process is *not* blocked on some other lock that we are + // holding, then it will eventually release the requested lock. + + nextSleep := 1 * time.Millisecond + const maxSleep = 500 * time.Millisecond + for { + err = setlkw(f.fh.Fd(), cmd, lt) + if !errors.Is(err, unix.EDEADLK) { + break + } + + time.Sleep(nextSleep) + + nextSleep += nextSleep + if nextSleep > maxSleep { + nextSleep = maxSleep + } + // Apply 10% jitter to avoid synchronizing collisions when we finally unblock. + nextSleep += time.Duration((0.1*rand.Float64() - 0.05) * float64(nextSleep)) + } + + if err != nil { + f.doUnlock() + + if cmd == tryLock && errors.Is(err, unix.EACCES) { + return false, nil + } + + return false, &fs.PathError{ + Op: lt.String(), + Path: f.Path(), + Err: err, + } + } + + return true, nil +} + +func (f *Flock) Unlock() error { + f.m.Lock() + defer f.m.Unlock() + + // If we aren't locked or if the lockfile instance is nil + // just return a nil error because we are unlocked. + if (!f.l && !f.r) || f.fh == nil { + return nil + } + + if err := f.doUnlock(); err != nil { + return err + } + + f.reset() + + return nil +} + +// https://github.com/golang/go/blob/09aeb6e33ab426eff4676a3baf694d5a3019e9fc/src/cmd/go/internal/lockedfile/internal/filelock/filelock_fcntl.go#L163 +func (f *Flock) doUnlock() (err error) { + var owner *Flock + + mu.Lock() + + ino, ok := inodes[f] + if ok { + owner = locks[ino].owner + } + + mu.Unlock() + + if owner == f { + err = setlkw(f.fh.Fd(), waitLock, unix.F_UNLCK) + } + + mu.Lock() + + l := locks[ino] + + if len(l.queue) == 0 { + // No waiters: remove the map entry. + delete(locks, ino) + } else { + // The first waiter is sending us their file now. + // Receive it and update the queue. + l.owner = <-l.queue[0] + l.queue = l.queue[1:] + locks[ino] = l + } + + delete(inodes, f) + + mu.Unlock() + + return err +} + +// TryLock is the preferred function for taking an exclusive file lock. +// This function takes an RW-mutex lock before it tries to lock the file, +// so there is the possibility that this function may block for a short time +// if another goroutine is trying to take any action. +// +// The actual file lock is non-blocking. +// If we are unable to get the exclusive file lock, +// the function will return false instead of waiting for the lock. +// If we get the lock, we also set the *Flock instance as being exclusive-locked. +func (f *Flock) TryLock() (bool, error) { + return f.try(&f.l, writeLock) +} + +// TryRLock is the preferred function for taking a shared file lock. +// This function takes an RW-mutex lock before it tries to lock the file, +// so there is the possibility that this function may block for a short time +// if another goroutine is trying to take any action. +// +// The actual file lock is non-blocking. +// If we are unable to get the shared file lock, +// the function will return false instead of waiting for the lock. +// If we get the lock, we also set the *Flock instance as being share-locked. +func (f *Flock) TryRLock() (bool, error) { + return f.try(&f.r, readLock) +} + +func (f *Flock) try(locked *bool, flag lockType) (bool, error) { + f.m.Lock() + defer f.m.Unlock() + + if *locked { + return true, nil + } + + if f.fh == nil { + if err := f.setFh(f.flag); err != nil { + return false, err + } + + defer f.ensureFhState() + } + + hasLock, err := f.doLock(tryLock, flag, false) + if err != nil { + return false, err + } + + *locked = hasLock + + return hasLock, nil +} + +// setlkw calls FcntlFlock with cmd for the entire file indicated by fd. +// https://github.com/golang/go/blob/09aeb6e33ab426eff4676a3baf694d5a3019e9fc/src/cmd/go/internal/lockedfile/internal/filelock/filelock_fcntl.go#L198 +func setlkw(fd uintptr, cmd cmdType, lt lockType) error { + for { + err := unix.FcntlFlock(fd, int(cmd), &unix.Flock_t{ + Type: int16(lt), + Whence: io.SeekStart, + Start: 0, + Len: 0, // All bytes. + }) + if !errors.Is(err, unix.EINTR) { + return err + } + } +} diff --git a/tools/vendor/github.com/gofrs/flock/flock_winapi.go b/tools/vendor/github.com/gofrs/flock/flock_winapi.go deleted file mode 100644 index fe405a255a..0000000000 --- a/tools/vendor/github.com/gofrs/flock/flock_winapi.go +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2015 Tim Heckman. All rights reserved. -// Use of this source code is governed by the BSD 3-Clause -// license that can be found in the LICENSE file. - -// +build windows - -package flock - -import ( - "syscall" - "unsafe" -) - -var ( - kernel32, _ = syscall.LoadLibrary("kernel32.dll") - procLockFileEx, _ = syscall.GetProcAddress(kernel32, "LockFileEx") - procUnlockFileEx, _ = syscall.GetProcAddress(kernel32, "UnlockFileEx") -) - -const ( - winLockfileFailImmediately = 0x00000001 - winLockfileExclusiveLock = 0x00000002 - winLockfileSharedLock = 0x00000000 -) - -// Use of 0x00000000 for the shared lock is a guess based on some the MS Windows -// `LockFileEX` docs, which document the `LOCKFILE_EXCLUSIVE_LOCK` flag as: -// -// > The function requests an exclusive lock. Otherwise, it requests a shared -// > lock. -// -// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365203(v=vs.85).aspx - -func lockFileEx(handle syscall.Handle, flags uint32, reserved uint32, numberOfBytesToLockLow uint32, numberOfBytesToLockHigh uint32, offset *syscall.Overlapped) (bool, syscall.Errno) { - r1, _, errNo := syscall.Syscall6( - uintptr(procLockFileEx), - 6, - uintptr(handle), - uintptr(flags), - uintptr(reserved), - uintptr(numberOfBytesToLockLow), - uintptr(numberOfBytesToLockHigh), - uintptr(unsafe.Pointer(offset))) - - if r1 != 1 { - if errNo == 0 { - return false, syscall.EINVAL - } - - return false, errNo - } - - return true, 0 -} - -func unlockFileEx(handle syscall.Handle, reserved uint32, numberOfBytesToLockLow uint32, numberOfBytesToLockHigh uint32, offset *syscall.Overlapped) (bool, syscall.Errno) { - r1, _, errNo := syscall.Syscall6( - uintptr(procUnlockFileEx), - 5, - uintptr(handle), - uintptr(reserved), - uintptr(numberOfBytesToLockLow), - uintptr(numberOfBytesToLockHigh), - uintptr(unsafe.Pointer(offset)), - 0) - - if r1 != 1 { - if errNo == 0 { - return false, syscall.EINVAL - } - - return false, errNo - } - - return true, 0 -} diff --git a/tools/vendor/github.com/gofrs/flock/flock_windows.go b/tools/vendor/github.com/gofrs/flock/flock_windows.go index ddb534ccef..dfd31e15f5 100644 --- a/tools/vendor/github.com/gofrs/flock/flock_windows.go +++ b/tools/vendor/github.com/gofrs/flock/flock_windows.go @@ -1,35 +1,48 @@ // Copyright 2015 Tim Heckman. All rights reserved. +// Copyright 2018-2024 The Gofrs. All rights reserved. // Use of this source code is governed by the BSD 3-Clause // license that can be found in the LICENSE file. +//go:build windows + package flock import ( - "syscall" + "errors" + + "golang.org/x/sys/windows" ) -// ErrorLockViolation is the error code returned from the Windows syscall when a -// lock would block and you ask to fail immediately. -const ErrorLockViolation syscall.Errno = 0x21 // 33 +// Use of 0x00000000 for the shared lock is a guess based on some the MS Windows `LockFileEX` docs, +// which document the `LOCKFILE_EXCLUSIVE_LOCK` flag as: +// +// > The function requests an exclusive lock. Otherwise, it requests a shared lock. +// +// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365203(v=vs.85).aspx +const winLockfileSharedLock = 0x00000000 + +// ErrorLockViolation is the error code returned from the Windows syscall when a lock would block, +// and you ask to fail immediately. +const ErrorLockViolation windows.Errno = 0x21 // 33 -// Lock is a blocking call to try and take an exclusive file lock. It will wait -// until it is able to obtain the exclusive file lock. It's recommended that -// TryLock() be used over this function. This function may block the ability to -// query the current Locked() or RLocked() status due to a RW-mutex lock. +// Lock is a blocking call to try and take an exclusive file lock. +// It will wait until it is able to obtain the exclusive file lock. +// It's recommended that TryLock() be used over this function. +// This function may block the ability to query the current Locked() or RLocked() status due to a RW-mutex lock. // -// If we are already locked, this function short-circuits and returns -// immediately assuming it can take the mutex lock. +// If we are already locked, this function short-circuits and +// returns immediately assuming it can take the mutex lock. func (f *Flock) Lock() error { - return f.lock(&f.l, winLockfileExclusiveLock) + return f.lock(&f.l, windows.LOCKFILE_EXCLUSIVE_LOCK) } -// RLock is a blocking call to try and take a shared file lock. It will wait -// until it is able to obtain the shared file lock. It's recommended that -// TryRLock() be used over this function. This function may block the ability to -// query the current Locked() or RLocked() status due to a RW-mutex lock. +// RLock is a blocking call to try and take a shared file lock. +// It will wait until it is able to obtain the shared file lock. +// It's recommended that TryRLock() be used over this function. +// This function may block the ability to query the current Locked() or RLocked() status due to a RW-mutex lock. // -// If we are already locked, this function short-circuits and returns -// immediately assuming it can take the mutex lock. +// If we are already locked, this function short-circuits and +// returns immediately assuming it can take the mutex lock. func (f *Flock) RLock() error { return f.lock(&f.r, winLockfileSharedLock) } @@ -43,26 +56,31 @@ func (f *Flock) lock(locked *bool, flag uint32) error { } if f.fh == nil { - if err := f.setFh(); err != nil { + if err := f.setFh(f.flag); err != nil { return err } + defer f.ensureFhState() } - if _, errNo := lockFileEx(syscall.Handle(f.fh.Fd()), flag, 0, 1, 0, &syscall.Overlapped{}); errNo > 0 { - return errNo + err := windows.LockFileEx(windows.Handle(f.fh.Fd()), flag, 0, 1, 0, &windows.Overlapped{}) + if err != nil && !errors.Is(err, windows.Errno(0)) { + return err } *locked = true + return nil } -// Unlock is a function to unlock the file. This file takes a RW-mutex lock, so -// while it is running the Locked() and RLocked() functions will be blocked. +// Unlock is a function to unlock the file. +// This file takes a RW-mutex lock, +// so while it is running the Locked() and RLocked() functions will be blocked. // -// This function short-circuits if we are unlocked already. If not, it calls -// UnlockFileEx() on the file and closes the file descriptor. It does not remove -// the file from disk. It's up to your application to do. +// This function short-circuits if we are unlocked already. +// If not, it calls UnlockFileEx() on the file and closes the file descriptor. +// It does not remove the file from disk. +// It's up to your application to do. func (f *Flock) Unlock() error { f.m.Lock() defer f.m.Unlock() @@ -74,39 +92,37 @@ func (f *Flock) Unlock() error { } // mark the file as unlocked - if _, errNo := unlockFileEx(syscall.Handle(f.fh.Fd()), 0, 1, 0, &syscall.Overlapped{}); errNo > 0 { - return errNo + err := windows.UnlockFileEx(windows.Handle(f.fh.Fd()), 0, 1, 0, &windows.Overlapped{}) + if err != nil && !errors.Is(err, windows.Errno(0)) { + return err } - f.fh.Close() - - f.l = false - f.r = false - f.fh = nil + f.reset() return nil } -// TryLock is the preferred function for taking an exclusive file lock. This -// function does take a RW-mutex lock before it tries to lock the file, so there -// is the possibility that this function may block for a short time if another -// goroutine is trying to take any action. +// TryLock is the preferred function for taking an exclusive file lock. +// This function does take a RW-mutex lock before it tries to lock the file, +// so there is the possibility that this function may block for a short time +// if another goroutine is trying to take any action. // -// The actual file lock is non-blocking. If we are unable to get the exclusive -// file lock, the function will return false instead of waiting for the lock. If -// we get the lock, we also set the *Flock instance as being exclusive-locked. +// The actual file lock is non-blocking. +// If we are unable to get the exclusive file lock, +// the function will return false instead of waiting for the lock. +// If we get the lock, we also set the *Flock instance as being exclusive-locked. func (f *Flock) TryLock() (bool, error) { - return f.try(&f.l, winLockfileExclusiveLock) + return f.try(&f.l, windows.LOCKFILE_EXCLUSIVE_LOCK) } -// TryRLock is the preferred function for taking a shared file lock. This -// function does take a RW-mutex lock before it tries to lock the file, so there -// is the possibility that this function may block for a short time if another -// goroutine is trying to take any action. +// TryRLock is the preferred function for taking a shared file lock. +// This function does take a RW-mutex lock before it tries to lock the file, +// so there is the possibility that this function may block for a short time if another goroutine is trying to take any action. // -// The actual file lock is non-blocking. If we are unable to get the shared file -// lock, the function will return false instead of waiting for the lock. If we -// get the lock, we also set the *Flock instance as being shared-locked. +// The actual file lock is non-blocking. +// If we are unable to get the shared file lock, +// the function will return false instead of waiting for the lock. +// If we get the lock, we also set the *Flock instance as being shared-locked. func (f *Flock) TryRLock() (bool, error) { return f.try(&f.r, winLockfileSharedLock) } @@ -120,20 +136,20 @@ func (f *Flock) try(locked *bool, flag uint32) (bool, error) { } if f.fh == nil { - if err := f.setFh(); err != nil { + if err := f.setFh(f.flag); err != nil { return false, err } + defer f.ensureFhState() } - _, errNo := lockFileEx(syscall.Handle(f.fh.Fd()), flag|winLockfileFailImmediately, 0, 1, 0, &syscall.Overlapped{}) - - if errNo > 0 { - if errNo == ErrorLockViolation || errNo == syscall.ERROR_IO_PENDING { + err := windows.LockFileEx(windows.Handle(f.fh.Fd()), flag|windows.LOCKFILE_FAIL_IMMEDIATELY, 0, 1, 0, &windows.Overlapped{}) + if err != nil && !errors.Is(err, windows.Errno(0)) { + if errors.Is(err, ErrorLockViolation) || errors.Is(err, windows.ERROR_IO_PENDING) { return false, nil } - return false, errNo + return false, err } *locked = true diff --git a/tools/vendor/github.com/golangci/gofmt/gofmt/gofmt.go b/tools/vendor/github.com/golangci/gofmt/gofmt/gofmt.go index be046f34cf..909d37657e 100644 --- a/tools/vendor/github.com/golangci/gofmt/gofmt/gofmt.go +++ b/tools/vendor/github.com/golangci/gofmt/gofmt/gofmt.go @@ -16,10 +16,12 @@ import ( "go/token" "io" "io/fs" + "math/rand" "os" "path/filepath" "runtime" "runtime/pprof" + "strconv" "strings" "github.com/golangci/gofmt/gofmt/internal/diff" @@ -233,12 +235,9 @@ func processFile(filename string, info fs.FileInfo, in io.Reader, r *reporter) e } fileSet := token.NewFileSet() - fragmentOk := false - if info == nil { - // If we are formatting stdin, we accept a program fragment in lieu of a - // complete source file. - fragmentOk = true - } + // If we are formatting stdin, we accept a program fragment in lieu of a + // complete source file. + fragmentOk := info == nil file, sourceAdj, indentAdj, err := parse(fileSet, filename, src, fragmentOk) if err != nil { return err @@ -272,21 +271,9 @@ func processFile(filename string, info fs.FileInfo, in io.Reader, r *reporter) e if info == nil { panic("-w should not have been allowed with stdin") } - // make a temporary backup before overwriting original + perm := info.Mode().Perm() - bakname, err := backupFile(filename+".", src, perm) - if err != nil { - return err - } - fdSem <- true - err = os.WriteFile(filename, res, perm) - <-fdSem - if err != nil { - os.Rename(bakname, filename) - return err - } - err = os.Remove(bakname) - if err != nil { + if err := writeFile(filename, src, res, perm, info.Size()); err != nil { return err } } @@ -470,32 +457,111 @@ func fileWeight(path string, info fs.FileInfo) int64 { return info.Size() } -const chmodSupported = runtime.GOOS != "windows" +// writeFile updates a file with the new formatted data. +func writeFile(filename string, orig, formatted []byte, perm fs.FileMode, size int64) error { + // Make a temporary backup file before rewriting the original file. + bakname, err := backupFile(filename, orig, perm) + if err != nil { + return err + } + + fdSem <- true + defer func() { <-fdSem }() + + fout, err := os.OpenFile(filename, os.O_WRONLY, perm) + if err != nil { + // We couldn't even open the file, so it should + // not have changed. + os.Remove(bakname) + return err + } + defer fout.Close() // for error paths + + restoreFail := func(err error) { + fmt.Fprintf(os.Stderr, "gofmt: %s: error restoring file to original: %v; backup in %s\n", filename, err, bakname) + } + + n, err := fout.Write(formatted) + if err == nil && int64(n) < size { + err = fout.Truncate(int64(n)) + } + + if err != nil { + // Rewriting the file failed. + + if n == 0 { + // Original file unchanged. + os.Remove(bakname) + return err + } + + // Try to restore the original contents. + + no, erro := fout.WriteAt(orig, 0) + if erro != nil { + // That failed too. + restoreFail(erro) + return err + } + + if no < n { + // Original file is shorter. Truncate. + if erro = fout.Truncate(int64(no)); erro != nil { + restoreFail(erro) + return err + } + } + + if erro := fout.Close(); erro != nil { + restoreFail(erro) + return err + } + + // Original contents restored. + os.Remove(bakname) + return err + } + + if err := fout.Close(); err != nil { + restoreFail(err) + return err + } + + // File updated. + os.Remove(bakname) + return nil +} // backupFile writes data to a new file named filename with permissions perm, -// with randomly chosen such that the file name is unique. backupFile returns // the chosen file name. func backupFile(filename string, data []byte, perm fs.FileMode) (string, error) { fdSem <- true defer func() { <-fdSem }() - // create backup file - f, err := os.CreateTemp(filepath.Dir(filename), filepath.Base(filename)) - if err != nil { - return "", err + nextRandom := func() string { + return strconv.Itoa(rand.Int()) } - bakname := f.Name() - if chmodSupported { - err = f.Chmod(perm) - if err != nil { - f.Close() - os.Remove(bakname) - return bakname, err + + dir, base := filepath.Split(filename) + var ( + bakname string + f *os.File + ) + for { + bakname = filepath.Join(dir, base+"."+nextRandom()) + var err error + f, err = os.OpenFile(bakname, os.O_RDWR|os.O_CREATE|os.O_EXCL, perm) + if err == nil { + break + } + if err != nil && !os.IsExist(err) { + return "", err } } // write data to backup file - _, err = f.Write(data) + _, err := f.Write(data) if err1 := f.Close(); err == nil { err = err1 } diff --git a/tools/vendor/github.com/golangci/gofmt/gofmt/internal.go b/tools/vendor/github.com/golangci/gofmt/gofmt/internal.go index 31a825bf83..231a250915 100644 --- a/tools/vendor/github.com/golangci/gofmt/gofmt/internal.go +++ b/tools/vendor/github.com/golangci/gofmt/gofmt/internal.go @@ -27,11 +27,11 @@ func parse(fset *token.FileSet, filename string, src []byte, fragmentOk bool) ( err error, ) { - // START - Change related to usgae inside golangci-lint + // START - Change related to usage inside golangci-lint parserModeMu.Lock() parserMode := parserMode parserModeMu.Unlock() - // END - Change related to usgae inside golangci-lint + // END - Change related to usage inside golangci-lint // Try as whole source file. file, err = parser.ParseFile(fset, filename, src, parserMode) diff --git a/tools/vendor/github.com/golangci/gofmt/gofmt/internal/diff/diff.go b/tools/vendor/github.com/golangci/gofmt/gofmt/internal/diff/diff.go index 47b2856714..6a40b23fcb 100644 --- a/tools/vendor/github.com/golangci/gofmt/gofmt/internal/diff/diff.go +++ b/tools/vendor/github.com/golangci/gofmt/gofmt/internal/diff/diff.go @@ -74,9 +74,9 @@ func Diff(oldName string, old []byte, newName string, new []byte) []byte { continue } - // Expand matching lines as far possible, + // Expand matching lines as far as possible, // establishing that x[start.x:end.x] == y[start.y:end.y]. - // Note that on the first (or last) iteration we may (or definitey do) + // Note that on the first (or last) iteration we may (or definitely do) // have an empty match: start.x==end.x and start.y==end.y. start := m for start.x > done.x && start.y > done.y && x[start.x-1] == y[start.y-1] { diff --git a/tools/vendor/github.com/golangci/gofmt/gofmt/readme.md b/tools/vendor/github.com/golangci/gofmt/gofmt/readme.md index c2faaab82d..be08179e60 100644 --- a/tools/vendor/github.com/golangci/gofmt/gofmt/readme.md +++ b/tools/vendor/github.com/golangci/gofmt/gofmt/readme.md @@ -1,5 +1,16 @@ # Hard Fork of gofmt -2022-08-31: Sync with go1.18.5 -2023-10-04: Sync with go1.19.13 -2023-10-04: Sync with go1.20.8 +- https://github.com/golang/go/blob/master/src/cmd/gofmt/ +- https://github.com/golang/go/blob/master/src/internal/testenv +- https://github.com/golang/go/blob/master/src/internal/platform +- https://github.com/golang/go/blob/master/src/internal/txtar +- https://github.com/golang/go/blob/master/src/internal/diff +- https://github.com/golang/go/blob/master/src/internal/cfg + +## Updates + +- 2024-08-17: Sync with go1.22.6 +- 2023-02-28: Sync with go1.21.7 +- 2023-10-04: Sync with go1.20.8 +- 2023-10-04: Sync with go1.19.13 +- 2022-08-31: Sync with go1.18.5 diff --git a/tools/vendor/github.com/golangci/gofmt/gofmt/rewrite.go b/tools/vendor/github.com/golangci/gofmt/gofmt/rewrite.go index f1299a42b4..c95d44f61b 100644 --- a/tools/vendor/github.com/golangci/gofmt/gofmt/rewrite.go +++ b/tools/vendor/github.com/golangci/gofmt/gofmt/rewrite.go @@ -69,9 +69,7 @@ func rewriteFile(fileSet *token.FileSet, pattern, replace ast.Expr, p *ast.File) return reflect.Value{} } val = apply(rewriteVal, val) - for k := range m { - delete(m, k) - } + clear(m) if match(m, pat, val) { val = subst(m, repl, reflect.ValueOf(val.Interface().(ast.Node).Pos())) } @@ -199,7 +197,7 @@ func match(m map[string]reflect.Value, pattern, val reflect.Value) bool { // object pointers and token positions always match return true case callExprType: - // For calls, the Ellipsis fields (token.Position) must + // For calls, the Ellipsis fields (token.Pos) must // match since that is how f(x) and f(x...) are different. // Check them here but fall through for the remaining fields. p := pattern.Interface().(*ast.CallExpr) diff --git a/tools/vendor/github.com/golangci/gofmt/goimports/goimports.go b/tools/vendor/github.com/golangci/gofmt/goimports/goimports.go index 20d92e119c..556f2bd7ef 100644 --- a/tools/vendor/github.com/golangci/gofmt/goimports/goimports.go +++ b/tools/vendor/github.com/golangci/gofmt/goimports/goimports.go @@ -7,17 +7,16 @@ package goimports import ( "bytes" "fmt" - "io/ioutil" "os" "os/exec" "path/filepath" "runtime" ) -// Extracted from golang.org/x/tools@v0.13.0/cmd/goimports/goimports.go +// Extracted from golang.org/x/tools@v0.24.0/cmd/goimports/goimports.go func writeTempFile(dir, prefix string, data []byte) (string, error) { - file, err := ioutil.TempFile(dir, prefix) + file, err := os.CreateTemp(dir, prefix) if err != nil { return "", err } diff --git a/tools/vendor/github.com/golangci/gofmt/goimports/readme.md b/tools/vendor/github.com/golangci/gofmt/goimports/readme.md index e57ed550b1..23eecf82f3 100644 --- a/tools/vendor/github.com/golangci/gofmt/goimports/readme.md +++ b/tools/vendor/github.com/golangci/gofmt/goimports/readme.md @@ -1,4 +1,10 @@ # Hard Fork of goimports -2022-08-31: Sync with golang.org/x/tools v0.1.12 -2023-10-04: Sync with golang.org/x/tools v0.13.0 +- https://github.com/golang/tools/tree/master/cmd/goimports + +## Updates + +- 2024-08-17: Sync with golang.org/x/tools v0.24.0 +- 2024-02-28: Sync with golang.org/x/tools v0.18.0 +- 2023-10-04: Sync with golang.org/x/tools v0.13.0 +- 2022-08-31: Sync with golang.org/x/tools v0.1.12 diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/commands/config_verify.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/commands/config_verify.go index a44050b593..89017e9bfc 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/commands/config_verify.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/commands/config_verify.go @@ -140,7 +140,6 @@ func printValidationDetail(cmd *cobra.Command, detail *jsonschema.Detailed) { } for _, d := range detail.Errors { - d := d printValidationDetail(cmd, &d) } } diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/commands/internal/builder.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/commands/internal/builder.go index 7253615a45..f0e259fb02 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/commands/internal/builder.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/commands/internal/builder.go @@ -95,7 +95,7 @@ func (b Builder) clone(ctx context.Context) error { output, err := cmd.CombinedOutput() if err != nil { - b.log.Infof(string(output)) + b.log.Infof("%s", string(output)) return fmt.Errorf("%s: %w", strings.Join(cmd.Args, " "), err) } @@ -132,7 +132,7 @@ func (b Builder) goGet(ctx context.Context, plugin *Plugin) error { output, err := cmd.CombinedOutput() if err != nil { - b.log.Warnf(string(output)) + b.log.Warnf("%s", string(output)) return fmt.Errorf("%s: %w", strings.Join(cmd.Args, " "), err) } @@ -150,7 +150,7 @@ func (b Builder) addReplaceDirective(ctx context.Context, plugin *Plugin) error output, err := cmd.CombinedOutput() if err != nil { - b.log.Warnf(string(output)) + b.log.Warnf("%s", string(output)) return fmt.Errorf("%s: %w", strings.Join(cmd.Args, " "), err) } @@ -164,7 +164,7 @@ func (b Builder) goModTidy(ctx context.Context) error { output, err := cmd.CombinedOutput() if err != nil { - b.log.Warnf(string(output)) + b.log.Warnf("%s", string(output)) return fmt.Errorf("%s: %w", strings.Join(cmd.Args, " "), err) } @@ -187,7 +187,7 @@ func (b Builder) goBuild(ctx context.Context, binaryName string) error { output, err := cmd.CombinedOutput() if err != nil { - b.log.Warnf(string(output)) + b.log.Warnf("%s", string(output)) return fmt.Errorf("%s: %w", strings.Join(cmd.Args, " "), err) } diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/commands/run.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/commands/run.go index bc086bc3d7..f289bfdd79 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/commands/run.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/commands/run.go @@ -152,7 +152,7 @@ func (c *runCommand) persistentPreRunE(cmd *cobra.Command, args []string) error return err } - c.log.Infof(c.buildInfo.String()) + c.log.Infof("%s", c.buildInfo.String()) loader := config.NewLoader(c.log.Child(logutils.DebugKeyConfigReader), c.viper, cmd.Flags(), c.opts.LoaderOptions, c.cfg, args) diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/config/config.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/config/config.go index 1dd064013a..93b331bec4 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/config/config.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/config/config.go @@ -2,7 +2,6 @@ package config import ( "os" - "regexp" "strings" hcversion "github.com/hashicorp/go-version" @@ -89,22 +88,3 @@ func detectGoVersion() string { return "1.17" } - -// Trims the Go version to keep only M.m. -// Since Go 1.21 the version inside the go.mod can be a patched version (ex: 1.21.0). -// The version can also include information which we want to remove (ex: 1.21alpha1) -// https://go.dev/doc/toolchain#versions -// This a problem with staticcheck and gocritic. -func trimGoVersion(v string) string { - if v == "" { - return "" - } - - exp := regexp.MustCompile(`(\d\.\d+)(?:\.\d+|[a-z]+\d)`) - - if exp.MatchString(v) { - return exp.FindStringSubmatch(v)[1] - } - - return v -} diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/config/linters_settings.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/config/linters_settings.go index b2f4567d49..109de42431 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/config/linters_settings.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/config/linters_settings.go @@ -465,6 +465,7 @@ type GciSettings struct { Sections []string `mapstructure:"sections"` SkipGenerated bool `mapstructure:"skip-generated"` CustomOrder bool `mapstructure:"custom-order"` + NoLexOrder bool `mapstructure:"no-lex-order"` // Deprecated: use Sections instead. LocalPrefixes string `mapstructure:"local-prefixes"` @@ -716,7 +717,7 @@ type MustTagSettings struct { } type NakedretSettings struct { - MaxFuncLines int `mapstructure:"max-func-lines"` + MaxFuncLines uint `mapstructure:"max-func-lines"` } type NestifSettings struct { @@ -791,8 +792,9 @@ type ReassignSettings struct { } type ReviveSettings struct { - MaxOpenFiles int `mapstructure:"max-open-files"` - IgnoreGeneratedHeader bool `mapstructure:"ignore-generated-header"` + Go string `mapstructure:"-"` + MaxOpenFiles int `mapstructure:"max-open-files"` + IgnoreGeneratedHeader bool `mapstructure:"ignore-generated-header"` Confidence float64 Severity string EnableAllRules bool `mapstructure:"enable-all-rules"` @@ -879,6 +881,11 @@ type TestifylintSettings struct { ExpVarPattern string `mapstructure:"pattern"` } `mapstructure:"expected-actual"` + Formatter struct { + CheckFormatString *bool `mapstructure:"check-format-string"` + RequireFFuncs bool `mapstructure:"require-f-funcs"` + } `mapstructure:"formatter"` + GoRequire struct { IgnoreHTTPHandlers bool `mapstructure:"ignore-http-handlers"` } `mapstructure:"go-require"` @@ -942,7 +949,7 @@ type UnparamSettings struct { type UnusedSettings struct { FieldWritesAreUses bool `mapstructure:"field-writes-are-uses"` PostStatementsAreReads bool `mapstructure:"post-statements-are-reads"` - ExportedIsUsed bool `mapstructure:"exported-is-used"` + ExportedIsUsed bool `mapstructure:"exported-is-used"` // Deprecated ExportedFieldsAreUsed bool `mapstructure:"exported-fields-are-used"` ParametersAreUsed bool `mapstructure:"parameters-are-used"` LocalVariablesAreUsed bool `mapstructure:"local-variables-are-used"` diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/config/loader.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/config/loader.go index ea4cae2d55..efdbfce1f1 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/config/loader.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/config/loader.go @@ -14,6 +14,7 @@ import ( "github.com/golangci/golangci-lint/pkg/exitcodes" "github.com/golangci/golangci-lint/pkg/fsutils" + "github.com/golangci/golangci-lint/pkg/goutil" "github.com/golangci/golangci-lint/pkg/logutils" ) @@ -74,6 +75,11 @@ func (l *Loader) Load(opts LoadOptions) error { l.handleGoVersion() + err = goutil.CheckGoVersion(l.cfg.Run.Go) + if err != nil { + return err + } + err = l.handleEnableOnlyOption() if err != nil { return err @@ -290,7 +296,9 @@ func (l *Loader) handleGoVersion() { l.cfg.LintersSettings.Gofumpt.LangVersion = l.cfg.Run.Go } - trimmedGoVersion := trimGoVersion(l.cfg.Run.Go) + trimmedGoVersion := goutil.TrimGoVersion(l.cfg.Run.Go) + + l.cfg.LintersSettings.Revive.Go = trimmedGoVersion l.cfg.LintersSettings.Gocritic.Go = trimmedGoVersion @@ -431,6 +439,11 @@ func (l *Loader) handleLinterOptionDeprecations() { l.log.Warnf("The configuration option `linters.stylecheck.go` is deprecated, please use global `run.go`.") } + // Deprecated since v1.60.0 + if !l.cfg.LintersSettings.Unused.ExportedIsUsed { + l.log.Warnf("The configuration option `linters.unused.exported-is-used` is deprecated.") + } + // Deprecated since v1.58.0 if l.cfg.LintersSettings.SlogLint.ContextOnly { l.log.Warnf("The configuration option `linters.sloglint.context-only` is deprecated, please use `linters.sloglint.context`.") diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/config/output.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/config/output.go index 592e293e0b..6a26d5773e 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/config/output.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/config/output.go @@ -17,6 +17,7 @@ const ( OutFormatCodeClimate = "code-climate" OutFormatHTML = "html" OutFormatJunitXML = "junit-xml" + OutFormatJunitXMLExtended = "junit-xml-extended" OutFormatGithubActions = "github-actions" // Deprecated OutFormatTeamCity = "teamcity" OutFormatSarif = "sarif" @@ -32,6 +33,7 @@ var AllOutputFormats = []string{ OutFormatCodeClimate, OutFormatHTML, OutFormatJunitXML, + OutFormatJunitXMLExtended, OutFormatGithubActions, OutFormatTeamCity, OutFormatSarif, diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/goanalysis/pkgerrors/errors.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/goanalysis/pkgerrors/errors.go index 91f6dd39d7..7da659e803 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/goanalysis/pkgerrors/errors.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/goanalysis/pkgerrors/errors.go @@ -25,8 +25,6 @@ func BuildIssuesFromIllTypedError(errs []error, lintCtx *linter.Context) ([]resu var other error for _, err := range errs { - err := err - var ill *IllTypedError if !errors.As(err, &ill) { if other == nil { diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/goanalysis/runner_loadingpackage.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/goanalysis/runner_loadingpackage.go index c54357eb67..8abe2b6c1c 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/goanalysis/runner_loadingpackage.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/goanalysis/runner_loadingpackage.go @@ -16,6 +16,7 @@ import ( "golang.org/x/tools/go/packages" "github.com/golangci/golangci-lint/pkg/goanalysis/load" + "github.com/golangci/golangci-lint/pkg/goutil" "github.com/golangci/golangci-lint/pkg/logutils" ) @@ -150,12 +151,21 @@ func (lp *loadingPackage) loadFromSource(loadMode LoadMode) error { } return imp.Types, nil } + + // TODO(ldez) temporary workaround + rv, err := goutil.CleanRuntimeVersion() + if err != nil { + return err + } + tc := &types.Config{ Importer: importerFunc(importer), Error: func(err error) { pkg.Errors = append(pkg.Errors, lp.convertError(err)...) }, + GoVersion: rv, // TODO(ldez) temporary workaround } + _ = types.NewChecker(tc, pkg.Fset, pkg.Types, pkg.TypesInfo).Files(pkg.Syntax) // Don't handle error here: errors are adding by tc.Error function. @@ -470,7 +480,7 @@ func sizeOfReflectValueTreeBytes(rv reflect.Value, visitedPtrs map[uintptr]struc return sizeOfReflectValueTreeBytes(rv.Elem(), visitedPtrs) case reflect.Struct: ret := 0 - for i := 0; i < rv.NumField(); i++ { + for i := range rv.NumField() { ret += sizeOfReflectValueTreeBytes(rv.Field(i), visitedPtrs) } return ret diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/goanalysis/runners.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/goanalysis/runners.go index c02d33b797..79e52f52a3 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/goanalysis/runners.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/goanalysis/runners.go @@ -134,7 +134,7 @@ func saveIssuesToCache(allPkgs []*packages.Package, pkgsFromCache map[*packages. perPkgIssues[i.Pkg] = append(perPkgIssues[i.Pkg], *i) } - savedIssuesCount := int32(0) + var savedIssuesCount int64 = 0 lintResKey := getIssuesCacheKey(analyzers) workerCount := runtime.GOMAXPROCS(-1) @@ -162,7 +162,7 @@ func saveIssuesToCache(allPkgs []*packages.Package, pkgsFromCache map[*packages. }) } - atomic.AddInt32(&savedIssuesCount, int32(len(encodedIssues))) + atomic.AddInt64(&savedIssuesCount, int64(len(encodedIssues))) if err := lintCtx.PkgCache.Put(pkg, pkgcache.HashModeNeedAllDeps, lintResKey, encodedIssues); err != nil { lintCtx.Log.Infof("Failed to save package %s issues (%d) to cache: %s", pkg, len(pkgIssues), err) } else { @@ -205,7 +205,7 @@ func loadIssuesFromCache(pkgs []*packages.Package, lintCtx *linter.Context, wg.Add(workerCount) pkgCh := make(chan *packages.Package, len(pkgs)) - for i := 0; i < workerCount; i++ { + for range workerCount { go func() { defer wg.Done() for pkg := range pkgCh { diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/gci/gci.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/gci/gci.go index 38ed2a0330..a9afb6c893 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/gci/gci.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/gci/gci.go @@ -44,6 +44,7 @@ func New(settings *config.GciSettings) *goanalysis.Linter { Cfg: gcicfg.BoolConfig{ SkipGenerated: settings.SkipGenerated, CustomOrder: settings.CustomOrder, + NoLexOrder: settings.NoLexOrder, }, SectionStrings: settings.Sections, } @@ -195,7 +196,7 @@ func diffFormattedFilesToArray(paths []string, cfg gcicfg.Config, diffs *[]strin } // Code below this comment is borrowed and modified from gci. -// https://github.com/daixiang0/gci/blob/4725b0c101801e7449530eee2ddb0c72592e3405/pkg/config/config.go +// https://github.com/daixiang0/gci/blob/v0.13.5/pkg/config/config.go var defaultOrder = map[string]int{ section.StandardType: 0, @@ -229,10 +230,11 @@ func (g YamlConfig) Parse() (*gcicfg.Config, error) { sort.Slice(sections, func(i, j int) bool { sectionI, sectionJ := sections[i].Type(), sections[j].Type() - if strings.Compare(sectionI, sectionJ) == 0 { - return strings.Compare(sections[i].String(), sections[j].String()) < 0 + if g.origin.Cfg.NoLexOrder || strings.Compare(sectionI, sectionJ) != 0 { + return defaultOrder[sectionI] < defaultOrder[sectionJ] } - return defaultOrder[sectionI] < defaultOrder[sectionJ] + + return strings.Compare(sections[i].String(), sections[j].String()) < 0 }) } diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/gofumpt/gofumpt.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/gofumpt/gofumpt.go index 9a0bef84aa..3bb7df12e2 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/gofumpt/gofumpt.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/gofumpt/gofumpt.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "os" + "strings" "sync" "github.com/shazow/go-diff/difflib" @@ -113,10 +114,11 @@ func runGofumpt(lintCtx *linter.Context, pass *analysis.Pass, diff differ, optio func getLangVersion(settings *config.GofumptSettings) string { if settings == nil || settings.LangVersion == "" { - // TODO: defaults to "1.15", in the future (v2) must be set by using build.Default.ReleaseTags like staticcheck. - return "1.15" + // TODO: defaults to "1.15", in the future (v2) must be removed. + return "go1.15" } - return settings.LangVersion + + return "go" + strings.TrimPrefix(settings.LangVersion, "go") } func getIssuedTextGoFumpt(settings *config.LintersSettings) string { diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/gosec/gosec.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/gosec/gosec.go index c333152e69..a5367399b8 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/gosec/gosec.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/gosec/gosec.go @@ -10,6 +10,7 @@ import ( "sync" "github.com/securego/gosec/v2" + "github.com/securego/gosec/v2/analyzers" "github.com/securego/gosec/v2/issue" "github.com/securego/gosec/v2/rules" "golang.org/x/tools/go/analysis" @@ -27,16 +28,25 @@ func New(settings *config.GoSecSettings) *goanalysis.Linter { var mu sync.Mutex var resIssues []goanalysis.Issue - var filters []rules.RuleFilter conf := gosec.NewConfig() + + var ruleFilters []rules.RuleFilter + var analyzerFilters []analyzers.AnalyzerFilter if settings != nil { - filters = gosecRuleFilters(settings.Includes, settings.Excludes) + // TODO(ldez) to remove when the problem will be fixed by gosec. + // https://github.com/securego/gosec/issues/1211 + // https://github.com/securego/gosec/issues/1209 + settings.Excludes = append(settings.Excludes, "G407") + + ruleFilters = createRuleFilters(settings.Includes, settings.Excludes) + analyzerFilters = createAnalyzerFilters(settings.Includes, settings.Excludes) conf = toGosecConfig(settings) } logger := log.New(io.Discard, "", 0) - ruleDefinitions := rules.Generate(false, filters...) + ruleDefinitions := rules.Generate(false, ruleFilters...) + analyzerDefinitions := analyzers.Generate(false, analyzerFilters...) analyzer := &analysis.Analyzer{ Name: linterName, @@ -53,7 +63,9 @@ func New(settings *config.GoSecSettings) *goanalysis.Linter { analyzer.Run = func(pass *analysis.Pass) (any, error) { // The `gosecAnalyzer` is here because of concurrency issue. gosecAnalyzer := gosec.NewAnalyzer(conf, true, settings.ExcludeGenerated, false, settings.Concurrency, logger) + gosecAnalyzer.LoadRules(ruleDefinitions.RulesInfo()) + gosecAnalyzer.LoadAnalyzers(analyzerDefinitions.AnalyzersInfo()) issues := runGoSec(lintCtx, pass, settings, gosecAnalyzer) @@ -77,6 +89,7 @@ func runGoSec(lintCtx *linter.Context, pass *analysis.Pass, settings *config.GoS } analyzer.CheckRules(pkg) + analyzer.CheckAnalyzers(pkg) secIssues, _, _ := analyzer.Report() if len(secIssues) == 0 { @@ -175,8 +188,23 @@ func convertGosecGlobals(globalOptionFromConfig any, conf gosec.Config) { } } +// based on https://github.com/securego/gosec/blob/81cda2f91fbe1bf4735feb55febcae03e697a92b/cmd/gosec/main.go#L258-L275 +func createAnalyzerFilters(includes, excludes []string) []analyzers.AnalyzerFilter { + var filters []analyzers.AnalyzerFilter + + if len(includes) > 0 { + filters = append(filters, analyzers.NewAnalyzerFilter(false, includes...)) + } + + if len(excludes) > 0 { + filters = append(filters, analyzers.NewAnalyzerFilter(true, excludes...)) + } + + return filters +} + // based on https://github.com/securego/gosec/blob/569328eade2ccbad4ce2d0f21ee158ab5356a5cf/cmd/gosec/main.go#L170-L188 -func gosecRuleFilters(includes, excludes []string) []rules.RuleFilter { +func createRuleFilters(includes, excludes []string) []rules.RuleFilter { var filters []rules.RuleFilter if len(includes) > 0 { diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/gosimple/gosimple.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/gosimple/gosimple.go index 6a0d967232..c03871adf9 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/gosimple/gosimple.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/gosimple/gosimple.go @@ -11,7 +11,7 @@ import ( func New(settings *config.StaticCheckSettings) *goanalysis.Linter { cfg := internal.StaticCheckConfig(settings) - analyzers := internal.SetupStaticCheckAnalyzers(simple.Analyzers, internal.GetGoVersion(settings), cfg.Checks) + analyzers := internal.SetupStaticCheckAnalyzers(simple.Analyzers, cfg.Checks) return goanalysis.NewLinter( "gosimple", diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/govet/govet.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/govet/govet.go index 1211a8833b..eb63a5d334 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/govet/govet.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/govet/govet.go @@ -179,7 +179,7 @@ func analyzersFromConfig(settings *config.GovetSettings) []*analysis.Analyzer { } func isAnalyzerEnabled(name string, cfg *config.GovetSettings, defaultAnalyzers []*analysis.Analyzer) bool { - // TODO(ldez) remove loopclosure when go1.23 + // TODO(ldez) remove loopclosure when go1.24 if name == loopclosure.Analyzer.Name && config.IsGoGreaterThanOrEqual(cfg.Go, "1.22") { return false } diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/internal/diff.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/internal/diff.go index b20230dfa9..f919c5b2a8 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/internal/diff.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/internal/diff.go @@ -242,7 +242,6 @@ func ExtractIssuesFromPatch(patch string, lintCtx *linter.Context, linterName st changes := p.parse(hunk) for _, change := range changes { - change := change // fix scope i := result.Issue{ FromLinter: linterName, Pos: token.Position{ diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/internal/staticcheck_common.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/internal/staticcheck_common.go index 5b5812c318..958013d0df 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/internal/staticcheck_common.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/internal/staticcheck_common.go @@ -14,20 +14,7 @@ import ( var debugf = logutils.Debug(logutils.DebugKeyMegacheck) -func GetGoVersion(settings *config.StaticCheckSettings) string { - var goVersion string - if settings != nil { - goVersion = settings.GoVersion - } - - if goVersion != "" { - return goVersion - } - - return "1.17" -} - -func SetupStaticCheckAnalyzers(src []*lint.Analyzer, goVersion string, checks []string) []*analysis.Analyzer { +func SetupStaticCheckAnalyzers(src []*lint.Analyzer, checks []string) []*analysis.Analyzer { var names []string for _, a := range src { names = append(names, a.Analyzer.Name) @@ -38,7 +25,6 @@ func SetupStaticCheckAnalyzers(src []*lint.Analyzer, goVersion string, checks [] var ret []*analysis.Analyzer for _, a := range src { if filter[a.Analyzer.Name] { - SetAnalyzerGoVersion(a.Analyzer, goVersion) ret = append(ret, a.Analyzer) } } diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/intrange/intrange.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/intrange/intrange.go index a27569ebbc..d5ffd43453 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/intrange/intrange.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/intrange/intrange.go @@ -15,5 +15,5 @@ func New() *goanalysis.Linter { a.Doc, []*analysis.Analyzer{a}, nil, - ).WithLoadMode(goanalysis.LoadModeSyntax) + ).WithLoadMode(goanalysis.LoadModeTypesInfo) } diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/lll/lll.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/lll/lll.go index 15edcccad4..67f89eecbd 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/lll/lll.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/lll/lll.go @@ -121,7 +121,7 @@ func getLLLIssuesForFile(filename string, maxLineLen int, tabSpaces string) ([]r Filename: filename, Line: lineNumber, }, - Text: fmt.Sprintf("line is %d characters", lineLen), + Text: fmt.Sprintf("the line is %d characters long, which exceeds the maximum of %d characters.", lineLen, maxLineLen), FromLinter: linterName, }) } diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/nakedret/nakedret.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/nakedret/nakedret.go index 4dd3fd4c3f..beabf2cd8f 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/nakedret/nakedret.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/nakedret/nakedret.go @@ -9,12 +9,12 @@ import ( ) func New(settings *config.NakedretSettings) *goanalysis.Linter { - var maxLines int + var maxLines uint if settings != nil { maxLines = settings.MaxFuncLines } - a := nakedret.NakedReturnAnalyzer(uint(maxLines)) + a := nakedret.NakedReturnAnalyzer(maxLines) return goanalysis.NewLinter( a.Name, diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/nolintlint/internal/nolintlint.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/nolintlint/internal/nolintlint.go index 5fed41cfdf..08dd743783 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/nolintlint/internal/nolintlint.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/nolintlint/internal/nolintlint.go @@ -252,12 +252,19 @@ func (l Linter) Run(fset *token.FileSet, nodes ...ast.Node) ([]Issue, error) { // when detecting unused directives, we send all the directives through and filter them out in the nolint processor if (l.needs & NeedsUnused) != 0 { - removeNolintCompletely := &result.Replacement{ - Inline: &result.InlineFix{ - StartCol: pos.Column - 1, + removeNolintCompletely := &result.Replacement{} + + startCol := pos.Column - 1 + + if startCol == 0 { + // if the directive starts from a new line, remove the line + removeNolintCompletely.NeedOnlyDelete = true + } else { + removeNolintCompletely.Inline = &result.InlineFix{ + StartCol: startCol, Length: end.Column - pos.Column, NewString: "", - }, + } } if len(linters) == 0 { diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/revive/revive.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/revive/revive.go index da44d92414..90ce15db63 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/revive/revive.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/revive/revive.go @@ -10,6 +10,7 @@ import ( "sync" "github.com/BurntSushi/toml" + hcversion "github.com/hashicorp/go-version" reviveConfig "github.com/mgechev/revive/config" "github.com/mgechev/revive/lint" "github.com/mgechev/revive/rule" @@ -49,8 +50,14 @@ func New(settings *config.ReviveSettings) *goanalysis.Linter { []*analysis.Analyzer{analyzer}, nil, ).WithContextSetter(func(lintCtx *linter.Context) { + w, err := newWrapper(settings) + if err != nil { + lintCtx.Log.Errorf("setup revive: %v", err) + return + } + analyzer.Run = func(pass *analysis.Pass) (any, error) { - issues, err := runRevive(lintCtx, pass, settings) + issues, err := w.run(lintCtx, pass) if err != nil { return nil, err } @@ -70,27 +77,46 @@ func New(settings *config.ReviveSettings) *goanalysis.Linter { }).WithLoadMode(goanalysis.LoadModeSyntax) } -func runRevive(lintCtx *linter.Context, pass *analysis.Pass, settings *config.ReviveSettings) ([]goanalysis.Issue, error) { - packages := [][]string{internal.GetFileNames(pass)} +type wrapper struct { + revive lint.Linter + formatter lint.Formatter + lintingRules []lint.Rule + conf *lint.Config +} - conf, err := getReviveConfig(settings) +func newWrapper(settings *config.ReviveSettings) (*wrapper, error) { + conf, err := getConfig(settings) if err != nil { return nil, err } - formatter, err := reviveConfig.GetFormatter("json") + conf.GoVersion, err = hcversion.NewVersion(settings.Go) if err != nil { return nil, err } - revive := lint.New(os.ReadFile, settings.MaxOpenFiles) + formatter, err := reviveConfig.GetFormatter("json") + if err != nil { + return nil, err + } lintingRules, err := reviveConfig.GetLintingRules(conf, []lint.Rule{}) if err != nil { return nil, err } - failures, err := revive.Lint(packages, lintingRules, *conf) + return &wrapper{ + revive: lint.New(os.ReadFile, settings.MaxOpenFiles), + formatter: formatter, + lintingRules: lintingRules, + conf: conf, + }, nil +} + +func (w *wrapper) run(lintCtx *linter.Context, pass *analysis.Pass) ([]goanalysis.Issue, error) { + packages := [][]string{internal.GetFileNames(pass)} + + failures, err := w.revive.Lint(packages, w.lintingRules, *w.conf) if err != nil { return nil, err } @@ -100,7 +126,7 @@ func runRevive(lintCtx *linter.Context, pass *analysis.Pass, settings *config.Re var output string go func() { - output, err = formatter.Format(formatChan, *conf) + output, err = w.formatter.Format(formatChan, *w.conf) if err != nil { lintCtx.Log.Errorf("Format error: %v", err) } @@ -108,7 +134,7 @@ func runRevive(lintCtx *linter.Context, pass *analysis.Pass, settings *config.Re }() for f := range failures { - if f.Confidence < conf.Confidence { + if f.Confidence < w.conf.Confidence { continue } @@ -126,13 +152,13 @@ func runRevive(lintCtx *linter.Context, pass *analysis.Pass, settings *config.Re var issues []goanalysis.Issue for i := range results { - issues = append(issues, reviveToIssue(pass, &results[i])) + issues = append(issues, toIssue(pass, &results[i])) } return issues, nil } -func reviveToIssue(pass *analysis.Pass, object *jsonObject) goanalysis.Issue { +func toIssue(pass *analysis.Pass, object *jsonObject) goanalysis.Issue { lineRangeTo := object.Position.End.Line if object.RuleName == (&rule.ExportedRule{}).Name() { lineRangeTo = object.Position.Start.Line @@ -160,10 +186,13 @@ func reviveToIssue(pass *analysis.Pass, object *jsonObject) goanalysis.Issue { // https://github.com/golangci/golangci-lint/issues/1745 // https://github.com/mgechev/revive/blob/v1.3.7/config/config.go#L217 // https://github.com/mgechev/revive/blob/v1.3.7/config/config.go#L169-L174 -func getReviveConfig(cfg *config.ReviveSettings) (*lint.Config, error) { +func getConfig(cfg *config.ReviveSettings) (*lint.Config, error) { conf := defaultConfig() - if !reflect.DeepEqual(cfg, &config.ReviveSettings{}) { + // Since the Go version is dynamic, this value must be neutralized in order to compare with a "zero value" of the configuration structure. + zero := &config.ReviveSettings{Go: cfg.Go} + + if !reflect.DeepEqual(cfg, zero) { rawRoot := createConfigMap(cfg) buf := bytes.NewBuffer(nil) @@ -255,7 +284,7 @@ func safeTomlSlice(r []any) []any { } // This element is not exported by revive, so we need copy the code. -// Extracted from https://github.com/mgechev/revive/blob/v1.3.7/config/config.go#L15 +// Extracted from https://github.com/mgechev/revive/blob/v1.3.9/config/config.go#L15 var defaultRules = []lint.Rule{ &rule.VarDeclarationsRule{}, &rule.PackageCommentsRule{}, @@ -338,6 +367,7 @@ var allRules = append([]lint.Rule{ &rule.EnforceRepeatedArgTypeStyleRule{}, &rule.EnforceSliceStyleRule{}, &rule.MaxControlNestingRule{}, + &rule.CommentsDensityRule{}, }, defaultRules...) const defaultConfidence = 0.8 diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/staticcheck/staticcheck.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/staticcheck/staticcheck.go index 0c0534539e..79394bdb7f 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/staticcheck/staticcheck.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/staticcheck/staticcheck.go @@ -10,7 +10,7 @@ import ( func New(settings *config.StaticCheckSettings) *goanalysis.Linter { cfg := internal.StaticCheckConfig(settings) - analyzers := internal.SetupStaticCheckAnalyzers(staticcheck.Analyzers, internal.GetGoVersion(settings), cfg.Checks) + analyzers := internal.SetupStaticCheckAnalyzers(staticcheck.Analyzers, cfg.Checks) return goanalysis.NewLinter( "staticcheck", diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/stylecheck/stylecheck.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/stylecheck/stylecheck.go index b8fc8fe547..60859f28af 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/stylecheck/stylecheck.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/stylecheck/stylecheck.go @@ -20,7 +20,7 @@ func New(settings *config.StaticCheckSettings) *goanalysis.Linter { return cfg, nil } - analyzers := internal.SetupStaticCheckAnalyzers(stylecheck.Analyzers, internal.GetGoVersion(settings), cfg.Checks) + analyzers := internal.SetupStaticCheckAnalyzers(stylecheck.Analyzers, cfg.Checks) return goanalysis.NewLinter( "stylecheck", diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/testifylint/testifylint.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/testifylint/testifylint.go index a5f95a1e83..b3f2f0bd46 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/testifylint/testifylint.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/testifylint/testifylint.go @@ -18,6 +18,7 @@ func New(settings *config.TestifylintSettings) *goanalysis.Linter { "disable-all": settings.DisableAll, "bool-compare.ignore-custom-types": settings.BoolCompare.IgnoreCustomTypes, + "formatter.require-f-funcs": settings.Formatter.RequireFFuncs, "go-require.ignore-http-handlers": settings.GoRequire.IgnoreHTTPHandlers, } if len(settings.EnabledCheckers) > 0 { @@ -27,6 +28,9 @@ func New(settings *config.TestifylintSettings) *goanalysis.Linter { cfg[a.Name]["disable"] = settings.DisabledCheckers } + if b := settings.Formatter.CheckFormatString; b != nil { + cfg[a.Name]["formatter.check-format-string"] = *b + } if p := settings.ExpectedActual.ExpVarPattern; p != "" { cfg[a.Name]["expected-actual.pattern"] = p } diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/unused/unused.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/unused/unused.go index 55712f0840..7b2b478fc9 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/unused/unused.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/unused/unused.go @@ -12,14 +12,13 @@ import ( "github.com/golangci/golangci-lint/pkg/config" "github.com/golangci/golangci-lint/pkg/goanalysis" - "github.com/golangci/golangci-lint/pkg/golinters/internal" "github.com/golangci/golangci-lint/pkg/lint/linter" "github.com/golangci/golangci-lint/pkg/result" ) const linterName = "unused" -func New(settings *config.UnusedSettings, scSettings *config.StaticCheckSettings) *goanalysis.Linter { +func New(settings *config.UnusedSettings) *goanalysis.Linter { var mu sync.Mutex var resIssues []goanalysis.Issue @@ -41,8 +40,6 @@ func New(settings *config.UnusedSettings, scSettings *config.StaticCheckSettings }, } - internal.SetAnalyzerGoVersion(analyzer, internal.GetGoVersion(scSettings)) - return goanalysis.NewLinter( linterName, "Checks Go code for unused constants, variables, functions and types", @@ -90,11 +87,13 @@ func getUnusedResults(pass *analysis.Pass, settings *config.UnusedSettings) unus opts := unused.Options{ FieldWritesAreUses: settings.FieldWritesAreUses, PostStatementsAreReads: settings.PostStatementsAreReads, - ExportedIsUsed: settings.ExportedIsUsed, - ExportedFieldsAreUsed: settings.ExportedFieldsAreUsed, - ParametersAreUsed: settings.ParametersAreUsed, - LocalVariablesAreUsed: settings.LocalVariablesAreUsed, - GeneratedIsUsed: settings.GeneratedIsUsed, + // Related to https://github.com/golangci/golangci-lint/issues/4218 + // https://github.com/dominikh/go-tools/issues/1474#issuecomment-1850760813 + ExportedIsUsed: true, + ExportedFieldsAreUsed: settings.ExportedFieldsAreUsed, + ParametersAreUsed: settings.ParametersAreUsed, + LocalVariablesAreUsed: settings.LocalVariablesAreUsed, + GeneratedIsUsed: settings.GeneratedIsUsed, } // ref: https://github.com/dominikh/go-tools/blob/4ec1f474ca6c0feb8e10a8fcca4ab95f5b5b9881/internal/cmd/unused/unused.go#L68 diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/wsl/wsl.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/wsl/wsl.go index 5a72035b50..c728340ece 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/wsl/wsl.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/golinters/wsl/wsl.go @@ -25,6 +25,7 @@ func New(settings *config.WSLSettings) *goanalysis.Linter { ForceCuddleErrCheckAndAssign: settings.ForceCuddleErrCheckAndAssign, ErrorVariableNames: settings.ErrorVariableNames, ForceExclusiveShortDeclarations: settings.ForceExclusiveShortDeclarations, + IncludeGenerated: true, // force to true because golangci-lint already have a way to filter generated files. } } diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/goutil/version.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/goutil/version.go new file mode 100644 index 0000000000..4f42ebd1bf --- /dev/null +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/goutil/version.go @@ -0,0 +1,75 @@ +package goutil + +import ( + "fmt" + "go/version" + "regexp" + "runtime" + "strings" + + hcversion "github.com/hashicorp/go-version" +) + +func CheckGoVersion(goVersion string) error { + rv, err := CleanRuntimeVersion() + if err != nil { + return fmt.Errorf("clean runtime version: %w", err) + } + + langVersion := version.Lang(rv) + + runtimeVersion, err := hcversion.NewVersion(strings.TrimPrefix(langVersion, "go")) + if err != nil { + return err + } + + targetedVersion, err := hcversion.NewVersion(TrimGoVersion(goVersion)) + if err != nil { + return err + } + + if runtimeVersion.LessThan(targetedVersion) { + return fmt.Errorf("the Go language version (%s) used to build golangci-lint is lower than the targeted Go version (%s)", + langVersion, goVersion) + } + + return nil +} + +// TrimGoVersion Trims the Go version to keep only M.m. +// Since Go 1.21 the version inside the go.mod can be a patched version (ex: 1.21.0). +// The version can also include information which we want to remove (ex: 1.21alpha1) +// https://go.dev/doc/toolchain#versions +// This a problem with staticcheck and gocritic. +func TrimGoVersion(v string) string { + if v == "" { + return "" + } + + exp := regexp.MustCompile(`(\d\.\d+)(?:\.\d+|[a-z]+\d)`) + + if exp.MatchString(v) { + return exp.FindStringSubmatch(v)[1] + } + + return v +} + +func CleanRuntimeVersion() (string, error) { + return cleanRuntimeVersion(runtime.Version()) +} + +func cleanRuntimeVersion(rv string) (string, error) { + parts := strings.Fields(rv) + + for _, part := range parts { + // Allow to handle: + // - GOEXPERIMENT -> "go1.23.0 X:boringcrypto" + // - devel -> "devel go1.24-e705a2d Wed Aug 7 01:16:42 2024 +0000 linux/amd64" + if strings.HasPrefix(part, "go1.") { + return part, nil + } + } + + return "", fmt.Errorf("invalid Go runtime version: %s", rv) +} diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/lint/lintersdb/builder_linter.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/lint/lintersdb/builder_linter.go index a66f2eea09..c06cd9a038 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/lint/lintersdb/builder_linter.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/lint/lintersdb/builder_linter.go @@ -278,7 +278,8 @@ func (LinterBuilder) Build(cfg *config.Config) ([]*linter.Config, error) { WithSince("v1.28.0"). WithPresets(linter.PresetBugs). WithLoadForGoAnalysis(). - WithURL("https://github.com/kyoh86/exportloopref"), + WithURL("https://github.com/kyoh86/exportloopref"). + DeprecatedWarning("Since Go1.22 (loopvar) this linter is no longer relevant.", "v1.60.2", "copyloopvar"), linter.NewConfig(forbidigo.New(&cfg.LintersSettings.Forbidigo)). WithSince("v1.34.0"). @@ -507,6 +508,7 @@ func (LinterBuilder) Build(cfg *config.Config) ([]*linter.Config, error) { linter.NewConfig(intrange.New()). WithSince("v1.57.0"). + WithLoadForGoAnalysis(). WithPresets(linter.PresetStyle). WithURL("https://github.com/ckaznocha/intrange"). WithNoopFallback(cfg, linter.IsGoLowerThanGo122()), @@ -725,7 +727,7 @@ func (LinterBuilder) Build(cfg *config.Config) ([]*linter.Config, error) { linter.NewConfig(tenv.New(&cfg.LintersSettings.Tenv)). WithSince("v1.43.0"). - WithPresets(linter.PresetStyle). + WithPresets(linter.PresetTest). WithLoadForGoAnalysis(). WithURL("https://github.com/sivchari/tenv"), @@ -747,7 +749,7 @@ func (LinterBuilder) Build(cfg *config.Config) ([]*linter.Config, error) { linter.NewConfig(thelper.New(&cfg.LintersSettings.Thelper)). WithSince("v1.34.0"). - WithPresets(linter.PresetStyle). + WithPresets(linter.PresetTest). WithLoadForGoAnalysis(). WithURL("https://github.com/kulti/thelper"), @@ -774,7 +776,7 @@ func (LinterBuilder) Build(cfg *config.Config) ([]*linter.Config, error) { WithLoadForGoAnalysis(). WithURL("https://github.com/mvdan/unparam"), - linter.NewConfig(unused.New(&cfg.LintersSettings.Unused, &cfg.LintersSettings.Staticcheck)). + linter.NewConfig(unused.New(&cfg.LintersSettings.Unused)). WithEnabledByDefault(). WithSince("v1.20.0"). WithLoadForGoAnalysis(). diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/lint/lintersdb/builder_plugin_go.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/lint/lintersdb/builder_plugin_go.go index c6dbaf7930..88f3e2ae39 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/lint/lintersdb/builder_plugin_go.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/lint/lintersdb/builder_plugin_go.go @@ -43,8 +43,6 @@ func (b *PluginGoBuilder) Build(cfg *config.Config) ([]*linter.Config, error) { continue } - settings := settings - lc, err := b.loadConfig(cfg, name, &settings) if err != nil { return nil, fmt.Errorf("unable to load custom analyzer %q: %s, %w", name, settings.Path, err) diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/lint/lintersdb/manager.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/lint/lintersdb/manager.go index 0a487be92e..75ab53d7cf 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/lint/lintersdb/manager.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/lint/lintersdb/manager.go @@ -162,7 +162,6 @@ func (m *Manager) build(enabledByDefaultLinters []*linter.Config) map[string]*li // --presets can only add linters to default set for _, p := range m.cfg.Linters.Presets { for _, lc := range m.GetAllLinterConfigsForPreset(p) { - lc := lc resultLintersSet[lc.Name()] = lc } } diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/lint/runner.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/lint/runner.go index f583121ed8..c3b983ff6a 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/lint/runner.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/lint/runner.go @@ -115,7 +115,6 @@ func (r *Runner) Run(ctx context.Context, linters []*linter.Config) ([]result.Is ) for _, lc := range linters { - lc := lc sw.TrackStage(lc.Name(), func() { linterIssues, err := r.runLinterSafe(ctx, r.lintCtx, lc) if err != nil { @@ -189,7 +188,6 @@ func (r *Runner) processLintResults(inIssues []result.Issue) []result.Issue { // finalize processors: logging, clearing, no heavy work here for _, p := range r.Processors { - p := p sw.TrackStage(p.Name(), func() { p.Finish() }) @@ -208,11 +206,11 @@ func (r *Runner) printPerProcessorStat(stat map[string]processorStat) { parts := make([]string, 0, len(stat)) for name, ps := range stat { if ps.inCount != 0 { - parts = append(parts, fmt.Sprintf("%s: %d/%d", name, ps.outCount, ps.inCount)) + parts = append(parts, fmt.Sprintf("%s: %d/%d", name, ps.inCount, ps.outCount)) } } if len(parts) != 0 { - r.Log.Infof("Processors filtering stat (out/in): %s", strings.Join(parts, ", ")) + r.Log.Infof("Processors filtering stat (in/out): %s", strings.Join(parts, ", ")) } } @@ -220,7 +218,6 @@ func (r *Runner) processIssues(issues []result.Issue, sw *timeutils.Stopwatch, s for _, p := range r.Processors { var newIssues []result.Issue var err error - p := p sw.TrackStage(p.Name(), func() { newIssues, err = p.Process(issues) }) @@ -235,6 +232,7 @@ func (r *Runner) processIssues(issues []result.Issue, sw *timeutils.Stopwatch, s issues = newIssues } + // This is required by JSON serialization if issues == nil { issues = []result.Issue{} } diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/printers/junitxml.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/printers/junitxml.go index 3e3f82f580..7d0a703b0a 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/printers/junitxml.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/printers/junitxml.go @@ -30,6 +30,8 @@ type testCaseXML struct { Name string `xml:"name,attr"` ClassName string `xml:"classname,attr"` Failure failureXML `xml:"failure"` + File string `xml:"file,attr,omitempty"` + Line int `xml:"line,attr,omitempty"` } type failureXML struct { @@ -39,11 +41,15 @@ type failureXML struct { } type JunitXML struct { - w io.Writer + extended bool + w io.Writer } -func NewJunitXML(w io.Writer) *JunitXML { - return &JunitXML{w: w} +func NewJunitXML(extended bool, w io.Writer) *JunitXML { + return &JunitXML{ + extended: extended, + w: w, + } } func (p JunitXML) Print(issues []result.Issue) error { @@ -68,6 +74,11 @@ func (p JunitXML) Print(issues []result.Issue) error { }, } + if p.extended { + tc.File = i.Pos.Filename + tc.Line = i.Pos.Line + } + testSuite.TestCases = append(testSuite.TestCases, tc) suites[suiteName] = testSuite } diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/printers/printer.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/printers/printer.go index 53db01220e..20be02e015 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/printers/printer.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/printers/printer.go @@ -115,7 +115,7 @@ func (c *Printer) createPrinter(format string, w io.Writer) (issuePrinter, error switch format { case config.OutFormatJSON: p = NewJSON(c.reportData, w) - case config.OutFormatColoredLineNumber, config.OutFormatLineNumber: + case config.OutFormatLineNumber, config.OutFormatColoredLineNumber: p = NewText(c.cfg.PrintIssuedLine, format == config.OutFormatColoredLineNumber, c.cfg.PrintLinterName, c.log.Child(logutils.DebugKeyTextPrinter), w) @@ -129,8 +129,8 @@ func (c *Printer) createPrinter(format string, w io.Writer) (issuePrinter, error p = NewCodeClimate(w) case config.OutFormatHTML: p = NewHTML(w) - case config.OutFormatJunitXML: - p = NewJunitXML(w) + case config.OutFormatJunitXML, config.OutFormatJunitXMLExtended: + p = NewJunitXML(format == config.OutFormatJunitXMLExtended, w) case config.OutFormatGithubActions: p = NewGitHubAction(w) case config.OutFormatTeamCity: diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/printers/sarif.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/printers/sarif.go index 9ccf33ce19..8b1dd2ee29 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/printers/sarif.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/printers/sarif.go @@ -76,7 +76,12 @@ func (p Sarif) Print(issues []result.Issue) error { issue := issues[i] severity := issue.Severity - if severity == "" { + + switch severity { + // https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/sarif-v2.1.0-errata01-os-complete.html#_Toc141790898 + case "none", "note", "warning", "error": + // Valid levels. + default: severity = "error" } diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/result/processors/autogenerated_exclude.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/result/processors/autogenerated_exclude.go index 5cc5e530ce..82316f6a0a 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/result/processors/autogenerated_exclude.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/result/processors/autogenerated_exclude.go @@ -18,10 +18,19 @@ const ( AutogeneratedModeDisable = "disable" ) +// The values must be in lowercase. const ( genCodeGenerated = "code generated" genDoNotEdit = "do not edit" - genAutoFile = "autogenerated file" // easyjson + + // Related to easyjson. + genAutoFile = "autogenerated file" + + //nolint:lll // Long URL + // Related to Swagger Codegen. + // https://github.com/swagger-api/swagger-codegen/blob/61cfeac3b9d855b4eb8bffa0d118bece117bcb7d/modules/swagger-codegen/src/main/resources/go/partial_header.mustache#L16 + // https://github.com/swagger-api/swagger-codegen/issues/12358 + genSwaggerCodegen = "* generated by: swagger codegen " ) var _ Processor = (*AutogeneratedExclude)(nil) @@ -63,11 +72,6 @@ func (p *AutogeneratedExclude) Process(issues []result.Issue) ([]result.Issue, e func (*AutogeneratedExclude) Finish() {} func (p *AutogeneratedExclude) shouldPassIssue(issue *result.Issue) (bool, error) { - if issue.FromLinter == typeCheckName { - // don't hide typechecking errors in generated files: users expect to see why the project isn't compiling - return true, nil - } - if filepath.Base(issue.FilePath()) == "go.mod" { return true, nil } @@ -106,7 +110,7 @@ func (p *AutogeneratedExclude) shouldPassIssue(issue *result.Issue) (bool, error // The function uses a bit laxer rules than isGeneratedFileStrict to match more generated code. // See https://github.com/golangci/golangci-lint/issues/48 and https://github.com/golangci/golangci-lint/issues/72. func (p *AutogeneratedExclude) isGeneratedFileLax(doc string) bool { - markers := []string{genCodeGenerated, genDoNotEdit, genAutoFile} + markers := []string{genCodeGenerated, genDoNotEdit, genAutoFile, genSwaggerCodegen} doc = strings.ToLower(doc) diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/result/processors/exclude_rules.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/result/processors/exclude_rules.go index b468c51013..bf255ae82f 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/result/processors/exclude_rules.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/result/processors/exclude_rules.go @@ -64,7 +64,6 @@ func (p ExcludeRules) Process(issues []result.Issue) ([]result.Issue, error) { return filterIssues(issues, func(issue *result.Issue) bool { for _, rule := range p.rules { - rule := rule if rule.match(issue, p.files, p.log) { return false } diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/result/processors/invalid_issue.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/result/processors/invalid_issue.go index c1389e9707..3f6cfc540b 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/result/processors/invalid_issue.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/result/processors/invalid_issue.go @@ -22,7 +22,7 @@ func (InvalidIssue) Name() string { } func (p InvalidIssue) Process(issues []result.Issue) ([]result.Issue, error) { - tcIssues := filterIssues(issues, func(issue *result.Issue) bool { + tcIssues := filterIssuesUnsafe(issues, func(issue *result.Issue) bool { return issue.FromLinter == typeCheckName }) diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/result/processors/issues.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/result/processors/issues.go index a65b0c2b0c..ab443b87d7 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/result/processors/issues.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/result/processors/issues.go @@ -7,6 +7,23 @@ import ( ) func filterIssues(issues []result.Issue, filter func(issue *result.Issue) bool) []result.Issue { + retIssues := make([]result.Issue, 0, len(issues)) + for i := range issues { + if issues[i].FromLinter == typeCheckName { + // don't hide typechecking errors in generated files: users expect to see why the project isn't compiling + retIssues = append(retIssues, issues[i]) + continue + } + + if filter(&issues[i]) { + retIssues = append(retIssues, issues[i]) + } + } + + return retIssues +} + +func filterIssuesUnsafe(issues []result.Issue, filter func(issue *result.Issue) bool) []result.Issue { retIssues := make([]result.Issue, 0, len(issues)) for i := range issues { if filter(&issues[i]) { @@ -20,6 +37,12 @@ func filterIssues(issues []result.Issue, filter func(issue *result.Issue) bool) func filterIssuesErr(issues []result.Issue, filter func(issue *result.Issue) (bool, error)) ([]result.Issue, error) { retIssues := make([]result.Issue, 0, len(issues)) for i := range issues { + if issues[i].FromLinter == typeCheckName { + // don't hide typechecking errors in generated files: users expect to see why the project isn't compiling + retIssues = append(retIssues, issues[i]) + continue + } + ok, err := filter(&issues[i]) if err != nil { return nil, fmt.Errorf("can't filter issue %#v: %w", issues[i], err) diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/result/processors/max_from_linter.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/result/processors/max_from_linter.go index e6200eec4c..0680c3f296 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/result/processors/max_from_linter.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/result/processors/max_from_linter.go @@ -33,7 +33,7 @@ func (p *MaxFromLinter) Process(issues []result.Issue) ([]result.Issue, error) { return issues, nil } - return filterIssues(issues, func(issue *result.Issue) bool { + return filterIssuesUnsafe(issues, func(issue *result.Issue) bool { if issue.Replacement != nil && p.cfg.Issues.NeedFix { // we need to fix all issues at once => we need to return all of them return true diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/result/processors/max_per_file_from_linter.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/result/processors/max_per_file_from_linter.go index da9fe4b7df..a39c984734 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/result/processors/max_per_file_from_linter.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/result/processors/max_per_file_from_linter.go @@ -33,7 +33,7 @@ func (*MaxPerFileFromLinter) Name() string { } func (p *MaxPerFileFromLinter) Process(issues []result.Issue) ([]result.Issue, error) { - return filterIssues(issues, func(issue *result.Issue) bool { + return filterIssuesUnsafe(issues, func(issue *result.Issue) bool { limit := p.maxPerFileFromLinterConfig[issue.FromLinter] if limit == 0 { return true diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/result/processors/max_same_issues.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/result/processors/max_same_issues.go index 8948fa79db..1647cace09 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/result/processors/max_same_issues.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/result/processors/max_same_issues.go @@ -35,7 +35,7 @@ func (p *MaxSameIssues) Process(issues []result.Issue) ([]result.Issue, error) { return issues, nil } - return filterIssues(issues, func(issue *result.Issue) bool { + return filterIssuesUnsafe(issues, func(issue *result.Issue) bool { if issue.Replacement != nil && p.cfg.Issues.NeedFix { // we need to fix all issues at once => we need to return all of them return true diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/result/processors/sort_results.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/result/processors/sort_results.go index 77f58c03e5..4da73c72ea 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/result/processors/sort_results.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/result/processors/sort_results.go @@ -194,7 +194,7 @@ func mergeComparators(cmps []*comparator) (*comparator, error) { return nil, errors.New("no comparator") } - for i := 0; i < len(cmps)-1; i++ { + for i := range len(cmps) - 1 { findComparatorTip(cmps[i]).SetNext(cmps[i+1]) } diff --git a/tools/vendor/github.com/golangci/golangci-lint/pkg/result/processors/uniq_by_line.go b/tools/vendor/github.com/golangci/golangci-lint/pkg/result/processors/uniq_by_line.go index 8e384e390b..115196d9a7 100644 --- a/tools/vendor/github.com/golangci/golangci-lint/pkg/result/processors/uniq_by_line.go +++ b/tools/vendor/github.com/golangci/golangci-lint/pkg/result/processors/uniq_by_line.go @@ -30,7 +30,7 @@ func (p *UniqByLine) Process(issues []result.Issue) ([]result.Issue, error) { return issues, nil } - return filterIssues(issues, p.shouldPassIssue), nil + return filterIssuesUnsafe(issues, p.shouldPassIssue), nil } func (*UniqByLine) Finish() {} diff --git a/tools/vendor/github.com/jjti/go-spancheck/Makefile b/tools/vendor/github.com/jjti/go-spancheck/Makefile index 39d80f7c61..8e9d07be31 100644 --- a/tools/vendor/github.com/jjti/go-spancheck/Makefile +++ b/tools/vendor/github.com/jjti/go-spancheck/Makefile @@ -14,12 +14,12 @@ test: testvendor # Follow https://github.com/golang/go/issues/37054 for more details. .PHONY: testvendor testvendor: - @rm -rf base/src - @cd testdata/base && go mod vendor - @cp -r testdata/base/vendor testdata/base/src - @cp -r testdata/base/vendor testdata/disableerrorchecks/src - @cp -r testdata/base/vendor testdata/enableall/src - @rm -rf testdata/base/vendor + rm -rf testdata/base/src + cd testdata/base && GOWORK=off go mod vendor + cp -r testdata/base/vendor testdata/base/src + cp -r testdata/base/vendor testdata/disableerrorchecks/src + cp -r testdata/base/vendor testdata/enableall/src + rm -rf testdata/base/vendor .PHONY: install install: diff --git a/tools/vendor/github.com/jjti/go-spancheck/go.work.sum b/tools/vendor/github.com/jjti/go-spancheck/go.work.sum index 85e99bad5a..04eadf2c51 100644 --- a/tools/vendor/github.com/jjti/go-spancheck/go.work.sum +++ b/tools/vendor/github.com/jjti/go-spancheck/go.work.sum @@ -1,4 +1,4 @@ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= diff --git a/tools/vendor/github.com/jjti/go-spancheck/spancheck.go b/tools/vendor/github.com/jjti/go-spancheck/spancheck.go index d5d35a5b11..8fc7945c6b 100644 --- a/tools/vendor/github.com/jjti/go-spancheck/spancheck.go +++ b/tools/vendor/github.com/jjti/go-spancheck/spancheck.go @@ -389,7 +389,7 @@ func usesCall( // Selector (End, SetStatus, RecordError) hit. if n.Sel.Name == selName { id, ok := n.X.(*ast.Ident) - found = ok && id.Obj.Decl == sv.id.Obj.Decl + found = ok && id.Obj != nil && id.Obj.Decl == sv.id.Obj.Decl } // Check if an ignore signature matches. diff --git a/tools/vendor/github.com/mgechev/revive/config/config.go b/tools/vendor/github.com/mgechev/revive/config/config.go index 50a2b8966f..fed1d1913a 100644 --- a/tools/vendor/github.com/mgechev/revive/config/config.go +++ b/tools/vendor/github.com/mgechev/revive/config/config.go @@ -95,6 +95,7 @@ var allRules = append([]lint.Rule{ &rule.EnforceRepeatedArgTypeStyleRule{}, &rule.EnforceSliceStyleRule{}, &rule.MaxControlNestingRule{}, + &rule.CommentsDensityRule{}, }, defaultRules...) var allFormatters = []lint.Formatter{ diff --git a/tools/vendor/github.com/mgechev/revive/lint/config.go b/tools/vendor/github.com/mgechev/revive/lint/config.go index 7e51a93c28..d7ecd964aa 100644 --- a/tools/vendor/github.com/mgechev/revive/lint/config.go +++ b/tools/vendor/github.com/mgechev/revive/lint/config.go @@ -1,5 +1,9 @@ package lint +import ( + goversion "github.com/hashicorp/go-version" +) + // Arguments is type used for the arguments of a rule. type Arguments = []interface{} @@ -61,4 +65,7 @@ type Config struct { WarningCode int `toml:"warningCode"` Directives DirectivesConfig `toml:"directive"` Exclude []string `toml:"exclude"` + // If set, overrides the go language version specified in go.mod of + // packages being linted, and assumes this specific language version. + GoVersion *goversion.Version } diff --git a/tools/vendor/github.com/mgechev/revive/lint/linter.go b/tools/vendor/github.com/mgechev/revive/lint/linter.go index fb1ab6f28e..3c97f306f0 100644 --- a/tools/vendor/github.com/mgechev/revive/lint/linter.go +++ b/tools/vendor/github.com/mgechev/revive/lint/linter.go @@ -3,12 +3,18 @@ package lint import ( "bufio" "bytes" + "encoding/json" "fmt" "go/token" "os" + "os/exec" + "path/filepath" "regexp" "strconv" + "strings" "sync" + + goversion "github.com/hashicorp/go-version" ) // ReadFile defines an abstraction for reading files. @@ -57,16 +63,52 @@ var ( func (l *Linter) Lint(packages [][]string, ruleSet []Rule, config Config) (<-chan Failure, error) { failures := make(chan Failure) + perModVersions := make(map[string]*goversion.Version) + perPkgVersions := make([]*goversion.Version, len(packages)) + for n, files := range packages { + if len(files) == 0 { + continue + } + if config.GoVersion != nil { + perPkgVersions[n] = config.GoVersion + continue + } + + dir, err := filepath.Abs(filepath.Dir(files[0])) + if err != nil { + return nil, err + } + + alreadyKnownMod := false + for d, v := range perModVersions { + if strings.HasPrefix(dir, d) { + perPkgVersions[n] = v + alreadyKnownMod = true + break + } + } + if alreadyKnownMod { + continue + } + + d, v, err := detectGoMod(dir) + if err != nil { + return nil, err + } + perModVersions[d] = v + perPkgVersions[n] = v + } + var wg sync.WaitGroup - for _, pkg := range packages { + for n := range packages { wg.Add(1) - go func(pkg []string) { - if err := l.lintPackage(pkg, ruleSet, config, failures); err != nil { + go func(pkg []string, gover *goversion.Version) { + if err := l.lintPackage(pkg, gover, ruleSet, config, failures); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } defer wg.Done() - }(pkg) + }(packages[n], perPkgVersions[n]) } go func() { @@ -77,10 +119,15 @@ func (l *Linter) Lint(packages [][]string, ruleSet []Rule, config Config) (<-cha return failures, nil } -func (l *Linter) lintPackage(filenames []string, ruleSet []Rule, config Config, failures chan Failure) error { +func (l *Linter) lintPackage(filenames []string, gover *goversion.Version, ruleSet []Rule, config Config, failures chan Failure) error { + if len(filenames) == 0 { + return nil + } + pkg := &Package{ - fset: token.NewFileSet(), - files: map[string]*File{}, + fset: token.NewFileSet(), + files: map[string]*File{}, + goVersion: gover, } for _, filename := range filenames { content, err := l.readFile(filename) @@ -108,6 +155,40 @@ func (l *Linter) lintPackage(filenames []string, ruleSet []Rule, config Config, return nil } +func detectGoMod(dir string) (rootDir string, ver *goversion.Version, err error) { + // https://github.com/golang/go/issues/44753#issuecomment-790089020 + cmd := exec.Command("go", "list", "-m", "-json") + cmd.Dir = dir + + out, err := cmd.Output() + if err != nil { + return "", nil, fmt.Errorf("command go list: %w", err) + } + + // NOTE: A package may be part of a go workspace. In this case `go list -m` + // lists all modules in the workspace, so we need to go through them all. + d := json.NewDecoder(bytes.NewBuffer(out)) + for d.More() { + var v struct { + GoMod string `json:"GoMod"` + GoVersion string `json:"GoVersion"` + Dir string `json:"Dir"` + } + if err = d.Decode(&v); err != nil { + return "", nil, err + } + if v.GoMod == "" { + return "", nil, fmt.Errorf("not part of a module: %q", dir) + } + if v.Dir != "" && strings.HasPrefix(dir, v.Dir) { + rootDir = v.Dir + ver, err = goversion.NewVersion(strings.TrimPrefix(v.GoVersion, "go")) + return rootDir, ver, err + } + } + return "", nil, fmt.Errorf("not part of a module: %q", dir) +} + // isGenerated reports whether the source file is generated code // according the rules from https://golang.org/s/generatedcode. // This is inherited from the original go lint. diff --git a/tools/vendor/github.com/mgechev/revive/lint/package.go b/tools/vendor/github.com/mgechev/revive/lint/package.go index 5976acf99c..b4a0a72c79 100644 --- a/tools/vendor/github.com/mgechev/revive/lint/package.go +++ b/tools/vendor/github.com/mgechev/revive/lint/package.go @@ -7,13 +7,16 @@ import ( "go/types" "sync" + goversion "github.com/hashicorp/go-version" + "github.com/mgechev/revive/internal/typeparams" ) // Package represents a package in the project. type Package struct { - fset *token.FileSet - files map[string]*File + fset *token.FileSet + files map[string]*File + goVersion *goversion.Version typesPkg *types.Package typesInfo *types.Info @@ -29,6 +32,8 @@ var ( trueValue = 1 falseValue = 2 notSet = 3 + + go122 = goversion.Must(goversion.NewVersion("1.22")) ) // Files return package's files. @@ -188,3 +193,8 @@ func (p *Package) lint(rules []Rule, config Config, failures chan Failure) { } wg.Wait() } + +// IsAtLeastGo122 returns true if the Go version for this package is 1.22 or higher, false otherwise +func (p *Package) IsAtLeastGo122() bool { + return p.goVersion.GreaterThanOrEqual(go122) +} diff --git a/tools/vendor/github.com/mgechev/revive/rule/add-constant.go b/tools/vendor/github.com/mgechev/revive/rule/add-constant.go index 86182623a9..73dfa932cd 100644 --- a/tools/vendor/github.com/mgechev/revive/rule/add-constant.go +++ b/tools/vendor/github.com/mgechev/revive/rule/add-constant.go @@ -53,7 +53,7 @@ func (r *AddConstantRule) Apply(file *lint.File, arguments lint.Arguments) []lin onFailure: onFailure, strLits: make(map[string]int), strLitLimit: r.strLitLimit, - allowList: r.allowList, + allowList: r.allowList, ignoreFunctions: r.ignoreFunctions, structTags: make(map[*ast.BasicLit]struct{}), } @@ -72,7 +72,7 @@ type lintAddConstantRule struct { onFailure func(lint.Failure) strLits map[string]int strLitLimit int - allowList allowList + allowList allowList ignoreFunctions []*regexp.Regexp structTags map[*ast.BasicLit]struct{} } @@ -127,6 +127,11 @@ func (*lintAddConstantRule) getFuncName(expr *ast.CallExpr) string { switch prefix := f.X.(type) { case *ast.Ident: return prefix.Name + "." + f.Sel.Name + case *ast.CallExpr: + // If the selector is an CallExpr, like `fn().Info`, we return `.Info` as function name + if f.Sel != nil { + return "." + f.Sel.Name + } } case *ast.Ident: return f.Name diff --git a/tools/vendor/github.com/mgechev/revive/rule/comment-spacings.go b/tools/vendor/github.com/mgechev/revive/rule/comment-spacings.go index 2b8240ca58..bfb7eaf233 100644 --- a/tools/vendor/github.com/mgechev/revive/rule/comment-spacings.go +++ b/tools/vendor/github.com/mgechev/revive/rule/comment-spacings.go @@ -20,18 +20,13 @@ func (r *CommentSpacingsRule) configure(arguments lint.Arguments) { defer r.Unlock() if r.allowList == nil { - r.allowList = []string{ - "//go:", - "//revive:", - "//nolint:", - } - + r.allowList = []string{} for _, arg := range arguments { allow, ok := arg.(string) // Alt. non panicking version if !ok { panic(fmt.Sprintf("invalid argument %v for %s; expected string but got %T", arg, r.Name(), arg)) } - r.allowList = append(r.allowList, `//`+allow+`:`) + r.allowList = append(r.allowList, `//`+allow) } } } @@ -87,5 +82,5 @@ func (r *CommentSpacingsRule) isAllowed(line string) bool { } } - return false + return isDirectiveComment(line) } diff --git a/tools/vendor/github.com/mgechev/revive/rule/comments-density.go b/tools/vendor/github.com/mgechev/revive/rule/comments-density.go new file mode 100644 index 0000000000..5956fea237 --- /dev/null +++ b/tools/vendor/github.com/mgechev/revive/rule/comments-density.go @@ -0,0 +1,95 @@ +package rule + +import ( + "fmt" + "go/ast" + "strings" + "sync" + + "github.com/mgechev/revive/lint" +) + +// CommentsDensityRule lints given else constructs. +type CommentsDensityRule struct { + minimumCommentsDensity int64 + configured bool + sync.Mutex +} + +const defaultMinimumCommentsPercentage = 0 + +func (r *CommentsDensityRule) configure(arguments lint.Arguments) { + r.Lock() + defer r.Unlock() + + if r.configured { + return + } + + r.configured = true + + if len(arguments) < 1 { + r.minimumCommentsDensity = defaultMinimumCommentsPercentage + return + } + + var ok bool + r.minimumCommentsDensity, ok = arguments[0].(int64) + if !ok { + panic(fmt.Sprintf("invalid argument for %q rule: argument should be an int, got %T", r.Name(), arguments[0])) + } +} + +// Apply applies the rule to given file. +func (r *CommentsDensityRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure { + r.configure(arguments) + + commentsLines := countDocLines(file.AST.Comments) + statementsCount := countStatements(file.AST) + density := (float32(commentsLines) / float32(statementsCount+commentsLines)) * 100 + + if density < float32(r.minimumCommentsDensity) { + return []lint.Failure{ + { + Node: file.AST, + Confidence: 1, + Failure: fmt.Sprintf("the file has a comment density of %2.f%% (%d comment lines for %d code lines) but expected a minimum of %d%%", density, commentsLines, statementsCount, r.minimumCommentsDensity), + }, + } + } + + return nil +} + +// Name returns the rule name. +func (*CommentsDensityRule) Name() string { + return "comments-density" +} + +// countStatements counts the number of program statements in the given AST. +func countStatements(node ast.Node) int { + counter := 0 + + ast.Inspect(node, func(n ast.Node) bool { + switch n.(type) { + case *ast.ExprStmt, *ast.AssignStmt, *ast.ReturnStmt, *ast.GoStmt, *ast.DeferStmt, + *ast.BranchStmt, *ast.IfStmt, *ast.SwitchStmt, *ast.TypeSwitchStmt, + *ast.SelectStmt, *ast.ForStmt, *ast.RangeStmt, *ast.CaseClause, *ast.CommClause, + *ast.DeclStmt, *ast.FuncDecl: + counter++ + } + return true + }) + + return counter +} + +func countDocLines(comments []*ast.CommentGroup) int { + acc := 0 + for _, c := range comments { + lines := strings.Split(c.Text(), "\n") + acc += len(lines) - 1 + } + + return acc +} diff --git a/tools/vendor/github.com/mgechev/revive/rule/confusing-naming.go b/tools/vendor/github.com/mgechev/revive/rule/confusing-naming.go index febfd88245..32f6dd8035 100644 --- a/tools/vendor/github.com/mgechev/revive/rule/confusing-naming.go +++ b/tools/vendor/github.com/mgechev/revive/rule/confusing-naming.go @@ -138,16 +138,32 @@ func getStructName(r *ast.FieldList) string { switch v := t.(type) { case *ast.StarExpr: - t = v.X + return extractFromStarExpr(v) case *ast.IndexExpr: - t = v.X + return extractFromIndexExpr(v) + case *ast.Ident: + return v.Name } - if p, _ := t.(*ast.Ident); p != nil { - result = p.Name + return defaultStructName +} + +func extractFromStarExpr(expr *ast.StarExpr) string { + switch v := expr.X.(type) { + case *ast.IndexExpr: + return extractFromIndexExpr(v) + case *ast.Ident: + return v.Name } + return defaultStructName +} - return result +func extractFromIndexExpr(expr *ast.IndexExpr) string { + switch v := expr.X.(type) { + case *ast.Ident: + return v.Name + } + return defaultStructName } func checkStructFields(fields *ast.FieldList, structName string, w *lintConfusingNames) { diff --git a/tools/vendor/github.com/mgechev/revive/rule/datarace.go b/tools/vendor/github.com/mgechev/revive/rule/datarace.go index 39e96696ad..86ec6e1133 100644 --- a/tools/vendor/github.com/mgechev/revive/rule/datarace.go +++ b/tools/vendor/github.com/mgechev/revive/rule/datarace.go @@ -16,7 +16,7 @@ func (*DataRaceRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { onFailure := func(failure lint.Failure) { failures = append(failures, failure) } - w := lintDataRaces{onFailure: onFailure} + w := lintDataRaces{onFailure: onFailure, go122for: file.Pkg.IsAtLeastGo122()} ast.Walk(w, file.AST) @@ -30,6 +30,7 @@ func (*DataRaceRule) Name() string { type lintDataRaces struct { onFailure func(failure lint.Failure) + go122for bool } func (w lintDataRaces) Visit(n ast.Node) ast.Visitor { @@ -47,7 +48,7 @@ func (w lintDataRaces) Visit(n ast.Node) ast.Visitor { if results != nil { returnIDs = w.ExtractReturnIDs(results.List) } - fl := &lintFunctionForDataRaces{onFailure: w.onFailure, returnIDs: returnIDs, rangeIDs: map[*ast.Object]struct{}{}} + fl := &lintFunctionForDataRaces{onFailure: w.onFailure, returnIDs: returnIDs, rangeIDs: map[*ast.Object]struct{}{}, go122for: w.go122for} ast.Walk(fl, node.Body) return nil @@ -69,6 +70,7 @@ type lintFunctionForDataRaces struct { onFailure func(failure lint.Failure) returnIDs map[*ast.Object]struct{} rangeIDs map[*ast.Object]struct{} + go122for bool } func (w lintFunctionForDataRaces) Visit(node ast.Node) ast.Visitor { @@ -118,7 +120,7 @@ func (w lintFunctionForDataRaces) Visit(node ast.Node) ast.Visitor { _, isReturnID := w.returnIDs[id.Obj] switch { - case isRangeID: + case isRangeID && !w.go122for: w.onFailure(lint.Failure{ Confidence: 1, Node: id, diff --git a/tools/vendor/github.com/mgechev/revive/rule/enforce-slice-style.go b/tools/vendor/github.com/mgechev/revive/rule/enforce-slice-style.go index abaf20be0e..60d8ac0665 100644 --- a/tools/vendor/github.com/mgechev/revive/rule/enforce-slice-style.go +++ b/tools/vendor/github.com/mgechev/revive/rule/enforce-slice-style.go @@ -14,6 +14,7 @@ const ( enforceSliceStyleTypeAny enforceSliceStyleType = "any" enforceSliceStyleTypeMake enforceSliceStyleType = "make" enforceSliceStyleTypeLiteral enforceSliceStyleType = "literal" + enforceSliceStyleTypeNil enforceSliceStyleType = "nil" ) func sliceStyleFromString(s string) (enforceSliceStyleType, error) { @@ -24,6 +25,8 @@ func sliceStyleFromString(s string) (enforceSliceStyleType, error) { return enforceSliceStyleTypeMake, nil case string(enforceSliceStyleTypeLiteral): return enforceSliceStyleTypeLiteral, nil + case string(enforceSliceStyleTypeNil): + return enforceSliceStyleTypeNil, nil default: return enforceSliceStyleTypeAny, fmt.Errorf( "invalid slice style: %s (expecting one of %v)", @@ -32,6 +35,7 @@ func sliceStyleFromString(s string) (enforceSliceStyleType, error) { enforceSliceStyleTypeAny, enforceSliceStyleTypeMake, enforceSliceStyleTypeLiteral, + enforceSliceStyleTypeNil, }, ) } @@ -86,7 +90,10 @@ func (r *EnforceSliceStyleRule) Apply(file *lint.File, arguments lint.Arguments) ast.Inspect(astFile, func(n ast.Node) bool { switch v := n.(type) { case *ast.CompositeLit: - if r.enforceSliceStyle != enforceSliceStyleTypeMake { + switch r.enforceSliceStyle { + case enforceSliceStyleTypeMake, enforceSliceStyleTypeNil: + // continue + default: return true } @@ -99,14 +106,22 @@ func (r *EnforceSliceStyleRule) Apply(file *lint.File, arguments lint.Arguments) return true } + var failureMessage string + if r.enforceSliceStyle == enforceSliceStyleTypeNil { + failureMessage = "use nil slice declaration (e.g. var args []type) instead of []type{}" + } else { + failureMessage = "use make([]type) instead of []type{} (or declare nil slice)" + } failures = append(failures, lint.Failure{ Confidence: 1, Node: v, Category: "style", - Failure: "use make([]type) instead of []type{} (or declare nil slice)", + Failure: failureMessage, }) case *ast.CallExpr: - if r.enforceSliceStyle != enforceSliceStyleTypeLiteral { + switch r.enforceSliceStyle { + case enforceSliceStyleTypeLiteral, enforceSliceStyleTypeNil: + default: // skip any function calls, even if it's make([]type) // we don't want to report it if literals are not enforced return true @@ -151,11 +166,17 @@ func (r *EnforceSliceStyleRule) Apply(file *lint.File, arguments lint.Arguments) } } + var failureMessage string + if r.enforceSliceStyle == enforceSliceStyleTypeNil { + failureMessage = "use nil slice declaration (e.g. var args []type) instead of make([]type, 0)" + } else { + failureMessage = "use []type{} instead of make([]type, 0) (or declare nil slice)" + } failures = append(failures, lint.Failure{ Confidence: 1, Node: v.Args[0], Category: "style", - Failure: "use []type{} instead of make([]type, 0) (or declare nil slice)", + Failure: failureMessage, }) } return true diff --git a/tools/vendor/github.com/mgechev/revive/rule/optimize-operands-order.go b/tools/vendor/github.com/mgechev/revive/rule/optimize-operands-order.go index 841bde56c0..43d982d6b4 100644 --- a/tools/vendor/github.com/mgechev/revive/rule/optimize-operands-order.go +++ b/tools/vendor/github.com/mgechev/revive/rule/optimize-operands-order.go @@ -49,8 +49,17 @@ func (w lintOptimizeOperandsOrderlExpr) Visit(node ast.Node) ast.Visitor { } isCaller := func(n ast.Node) bool { - _, ok := n.(*ast.CallExpr) - return ok + ce, ok := n.(*ast.CallExpr) + if !ok { + return false + } + + ident, isIdent := ce.Fun.(*ast.Ident) + if !isIdent { + return true + } + + return ident.Name != "len" || ident.Obj != nil } // check if the left sub-expression contains a function call diff --git a/tools/vendor/github.com/mgechev/revive/rule/package-comments.go b/tools/vendor/github.com/mgechev/revive/rule/package-comments.go index 02f246be08..f1e5462fea 100644 --- a/tools/vendor/github.com/mgechev/revive/rule/package-comments.go +++ b/tools/vendor/github.com/mgechev/revive/rule/package-comments.go @@ -150,17 +150,9 @@ func (l *lintPackageComments) Visit(_ ast.Node) ast.Visitor { return nil } s := l.fileAst.Doc.Text() - if ts := strings.TrimLeft(s, " \t"); ts != s { - l.onFailure(lint.Failure{ - Category: "comments", - Node: l.fileAst.Doc, - Confidence: 1, - Failure: "package comment should not have leading space", - }) - s = ts - } + // Only non-main packages need to keep to this form. - if !l.file.Pkg.IsMain() && !strings.HasPrefix(s, prefix) { + if !l.file.Pkg.IsMain() && !strings.HasPrefix(s, prefix) && !isDirectiveComment(s) { l.onFailure(lint.Failure{ Category: "comments", Node: l.fileAst.Doc, diff --git a/tools/vendor/github.com/mgechev/revive/rule/range-val-address.go b/tools/vendor/github.com/mgechev/revive/rule/range-val-address.go index 51ad8e108b..d2ab0392ac 100644 --- a/tools/vendor/github.com/mgechev/revive/rule/range-val-address.go +++ b/tools/vendor/github.com/mgechev/revive/rule/range-val-address.go @@ -16,6 +16,10 @@ type RangeValAddress struct{} func (*RangeValAddress) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure + if file.Pkg.IsAtLeastGo122() { + return failures + } + walker := rangeValAddress{ file: file, onFailure: func(failure lint.Failure) { diff --git a/tools/vendor/github.com/mgechev/revive/rule/range-val-in-closure.go b/tools/vendor/github.com/mgechev/revive/rule/range-val-in-closure.go index 1e85d0d0d1..6f9255a74c 100644 --- a/tools/vendor/github.com/mgechev/revive/rule/range-val-in-closure.go +++ b/tools/vendor/github.com/mgechev/revive/rule/range-val-in-closure.go @@ -14,6 +14,10 @@ type RangeValInClosureRule struct{} func (*RangeValInClosureRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure + if file.Pkg.IsAtLeastGo122() { + return failures + } + walker := rangeValInClosure{ onFailure: func(failure lint.Failure) { failures = append(failures, failure) diff --git a/tools/vendor/github.com/mgechev/revive/rule/utils.go b/tools/vendor/github.com/mgechev/revive/rule/utils.go index 5778e76963..1267c2d392 100644 --- a/tools/vendor/github.com/mgechev/revive/rule/utils.go +++ b/tools/vendor/github.com/mgechev/revive/rule/utils.go @@ -165,3 +165,9 @@ func checkNumberOfArguments(expected int, args lint.Arguments, ruleName string) panic(fmt.Sprintf("not enough arguments for %s rule, expected %d, got %d. Please check the rule's documentation", ruleName, expected, len(args))) } } + +var directiveCommentRE = regexp.MustCompile("^//(line |extern |export |[a-z0-9]+:[a-z0-9])") // see https://go-review.googlesource.com/c/website/+/442516/1..2/_content/doc/comment.md#494 + +func isDirectiveComment(line string) bool { + return directiveCommentRE.MatchString(line) +} diff --git a/tools/vendor/github.com/moricho/tparallel/.goreleaser.yaml b/tools/vendor/github.com/moricho/tparallel/.goreleaser.yaml index 4a04fe25b5..5349004c58 100644 --- a/tools/vendor/github.com/moricho/tparallel/.goreleaser.yaml +++ b/tools/vendor/github.com/moricho/tparallel/.goreleaser.yaml @@ -1,7 +1,9 @@ project_name: tparallel + before: hooks: - go mod tidy + builds: - main: ./cmd/tparallel binary: tparallel @@ -19,15 +21,17 @@ builds: archives: - format: tar.gz name_template: >- - {{ .ProjectName }}_ + {{- .ProjectName }}_ {{- title .Os }}_ {{- if eq .Arch "amd64" }}x86_64 {{- else if eq .Arch "386" }}i386 {{- else }}{{ .Arch }}{{ end }} - {{- if .Arm }}v{{ .Arm }}{{ end }} + {{- if .Arm }}v{{ .Arm }}{{ end -}} + format_overrides: - - goos: windows - format: zip + - goos: windows + format: zip + checksum: name_template: 'checksums.txt' snapshot: @@ -38,10 +42,12 @@ changelog: exclude: - '^docs:' - '^test:' + release: prerelease: auto + brews: - - tap: + - repository: owner: moricho name: homebrew-tparallel homepage: https://github.com/moricho/tparallel @@ -49,4 +55,4 @@ brews: install: | bin.install "tparallel" test: | - system "#{bin}/goreleaser -v" + system "#{bin}/tparallel help" diff --git a/tools/vendor/github.com/moricho/tparallel/README.md b/tools/vendor/github.com/moricho/tparallel/README.md index 65ed46c422..c4f1efd01a 100644 --- a/tools/vendor/github.com/moricho/tparallel/README.md +++ b/tools/vendor/github.com/moricho/tparallel/README.md @@ -10,7 +10,7 @@ It detects the following: - `t.Parallel()` is called in either a top-level test function or a sub-test function only - Although `t.Parallel()` is called in the sub-test function, it is post-processed by `defer` instead of `t.Cleanup()` -This tool was inspired by this blog: [Go 言語でのテストの並列化 〜t.Parallel()メソッドを理解する〜](https://engineering.mercari.com/blog/entry/how_to_use_t_parallel/) +This tool was inspired by this blog: [Test parallelization in Go: Understanding the t.Parallel() method](https://engineering.mercari.com/en/blog/entry/20220408-how_to_use_t_parallel/) ## Installation diff --git a/tools/vendor/github.com/moricho/tparallel/testmap.go b/tools/vendor/github.com/moricho/tparallel/testmap.go index fa9bed7082..fd6a3b4326 100644 --- a/tools/vendor/github.com/moricho/tparallel/testmap.go +++ b/tools/vendor/github.com/moricho/tparallel/testmap.go @@ -49,6 +49,10 @@ func appendTestMap(subtests []*ssa.Function, instr ssa.Instruction) []*ssa.Funct } ssaCall := call.Value() + if ssaCall == nil { + return subtests + } + for _, arg := range ssaCall.Call.Args { switch arg := arg.(type) { case *ssa.Function: diff --git a/tools/vendor/github.com/pelletier/go-toml/v2/.goreleaser.yaml b/tools/vendor/github.com/pelletier/go-toml/v2/.goreleaser.yaml index 1d8b69e65e..ec52857a3e 100644 --- a/tools/vendor/github.com/pelletier/go-toml/v2/.goreleaser.yaml +++ b/tools/vendor/github.com/pelletier/go-toml/v2/.goreleaser.yaml @@ -1,3 +1,4 @@ +version: 2 before: hooks: - go mod tidy diff --git a/tools/vendor/github.com/pelletier/go-toml/v2/README.md b/tools/vendor/github.com/pelletier/go-toml/v2/README.md index d964b25fe1..0755e55642 100644 --- a/tools/vendor/github.com/pelletier/go-toml/v2/README.md +++ b/tools/vendor/github.com/pelletier/go-toml/v2/README.md @@ -565,7 +565,7 @@ complete solutions exist out there. ## Versioning -Expect for parts explicitely marked otherwise, go-toml follows [Semantic +Expect for parts explicitly marked otherwise, go-toml follows [Semantic Versioning](https://semver.org). The supported version of [TOML](https://github.com/toml-lang/toml) is indicated at the beginning of this document. The last two major versions of Go are supported (see [Go Release diff --git a/tools/vendor/github.com/pelletier/go-toml/v2/marshaler.go b/tools/vendor/github.com/pelletier/go-toml/v2/marshaler.go index 7f4e20c128..161acd9343 100644 --- a/tools/vendor/github.com/pelletier/go-toml/v2/marshaler.go +++ b/tools/vendor/github.com/pelletier/go-toml/v2/marshaler.go @@ -8,7 +8,7 @@ import ( "io" "math" "reflect" - "sort" + "slices" "strconv" "strings" "time" @@ -280,7 +280,7 @@ func (enc *Encoder) encode(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, e } hasTextMarshaler := v.Type().Implements(textMarshalerType) - if hasTextMarshaler || (v.CanAddr() && reflect.PtrTo(v.Type()).Implements(textMarshalerType)) { + if hasTextMarshaler || (v.CanAddr() && reflect.PointerTo(v.Type()).Implements(textMarshalerType)) { if !hasTextMarshaler { v = v.Addr() } @@ -631,6 +631,18 @@ func (enc *Encoder) keyToString(k reflect.Value) (string, error) { return "", fmt.Errorf("toml: error marshalling key %v from text: %w", k, err) } return string(keyB), nil + + case keyType.Kind() == reflect.Int || keyType.Kind() == reflect.Int8 || keyType.Kind() == reflect.Int16 || keyType.Kind() == reflect.Int32 || keyType.Kind() == reflect.Int64: + return strconv.FormatInt(k.Int(), 10), nil + + case keyType.Kind() == reflect.Uint || keyType.Kind() == reflect.Uint8 || keyType.Kind() == reflect.Uint16 || keyType.Kind() == reflect.Uint32 || keyType.Kind() == reflect.Uint64: + return strconv.FormatUint(k.Uint(), 10), nil + + case keyType.Kind() == reflect.Float32: + return strconv.FormatFloat(k.Float(), 'f', -1, 32), nil + + case keyType.Kind() == reflect.Float64: + return strconv.FormatFloat(k.Float(), 'f', -1, 64), nil } return "", fmt.Errorf("toml: type %s is not supported as a map key", keyType.Kind()) } @@ -668,8 +680,8 @@ func (enc *Encoder) encodeMap(b []byte, ctx encoderCtx, v reflect.Value) ([]byte } func sortEntriesByKey(e []entry) { - sort.Slice(e, func(i, j int) bool { - return e[i].Key < e[j].Key + slices.SortFunc(e, func(a, b entry) int { + return strings.Compare(a.Key, b.Key) }) } @@ -732,7 +744,7 @@ func walkStruct(ctx encoderCtx, t *table, v reflect.Value) { if fieldType.Anonymous { if fieldType.Type.Kind() == reflect.Struct { walkStruct(ctx, t, f) - } else if fieldType.Type.Kind() == reflect.Pointer && !f.IsNil() && f.Elem().Kind() == reflect.Struct { + } else if fieldType.Type.Kind() == reflect.Ptr && !f.IsNil() && f.Elem().Kind() == reflect.Struct { walkStruct(ctx, t, f.Elem()) } continue @@ -951,7 +963,7 @@ func willConvertToTable(ctx encoderCtx, v reflect.Value) bool { if !v.IsValid() { return false } - if v.Type() == timeType || v.Type().Implements(textMarshalerType) || (v.Kind() != reflect.Ptr && v.CanAddr() && reflect.PtrTo(v.Type()).Implements(textMarshalerType)) { + if v.Type() == timeType || v.Type().Implements(textMarshalerType) || (v.Kind() != reflect.Ptr && v.CanAddr() && reflect.PointerTo(v.Type()).Implements(textMarshalerType)) { return false } diff --git a/tools/vendor/github.com/pelletier/go-toml/v2/unmarshaler.go b/tools/vendor/github.com/pelletier/go-toml/v2/unmarshaler.go index 98231bae65..c3df8bee1c 100644 --- a/tools/vendor/github.com/pelletier/go-toml/v2/unmarshaler.go +++ b/tools/vendor/github.com/pelletier/go-toml/v2/unmarshaler.go @@ -5,9 +5,9 @@ import ( "errors" "fmt" "io" - "io/ioutil" "math" "reflect" + "strconv" "strings" "sync/atomic" "time" @@ -21,10 +21,8 @@ import ( // // It is a shortcut for Decoder.Decode() with the default options. func Unmarshal(data []byte, v interface{}) error { - p := unstable.Parser{} - p.Reset(data) - d := decoder{p: &p} - + d := decoder{} + d.p.Reset(data) return d.FromParser(v) } @@ -117,27 +115,25 @@ func (d *Decoder) EnableUnmarshalerInterface() *Decoder { // Inline Table -> same as Table // Array of Tables -> same as Array and Table func (d *Decoder) Decode(v interface{}) error { - b, err := ioutil.ReadAll(d.r) + b, err := io.ReadAll(d.r) if err != nil { return fmt.Errorf("toml: %w", err) } - p := unstable.Parser{} - p.Reset(b) dec := decoder{ - p: &p, strict: strict{ Enabled: d.strict, }, unmarshalerInterface: d.unmarshalerInterface, } + dec.p.Reset(b) return dec.FromParser(v) } type decoder struct { // Which parser instance in use for this decoding session. - p *unstable.Parser + p unstable.Parser // Flag indicating that the current expression is stashed. // If set to true, calling nextExpr will not actually pull a new expression @@ -1078,12 +1074,39 @@ func (d *decoder) keyFromData(keyType reflect.Type, data []byte) (reflect.Value, } return mk, nil - case reflect.PtrTo(keyType).Implements(textUnmarshalerType): + case reflect.PointerTo(keyType).Implements(textUnmarshalerType): mk := reflect.New(keyType) if err := mk.Interface().(encoding.TextUnmarshaler).UnmarshalText(data); err != nil { return reflect.Value{}, fmt.Errorf("toml: error unmarshalling key type %s from text: %w", stringType, err) } return mk.Elem(), nil + + case keyType.Kind() == reflect.Int || keyType.Kind() == reflect.Int8 || keyType.Kind() == reflect.Int16 || keyType.Kind() == reflect.Int32 || keyType.Kind() == reflect.Int64: + key, err := strconv.ParseInt(string(data), 10, 64) + if err != nil { + return reflect.Value{}, fmt.Errorf("toml: error parsing key of type %s from integer: %w", stringType, err) + } + return reflect.ValueOf(key).Convert(keyType), nil + case keyType.Kind() == reflect.Uint || keyType.Kind() == reflect.Uint8 || keyType.Kind() == reflect.Uint16 || keyType.Kind() == reflect.Uint32 || keyType.Kind() == reflect.Uint64: + key, err := strconv.ParseUint(string(data), 10, 64) + if err != nil { + return reflect.Value{}, fmt.Errorf("toml: error parsing key of type %s from unsigned integer: %w", stringType, err) + } + return reflect.ValueOf(key).Convert(keyType), nil + + case keyType.Kind() == reflect.Float32: + key, err := strconv.ParseFloat(string(data), 32) + if err != nil { + return reflect.Value{}, fmt.Errorf("toml: error parsing key of type %s from float: %w", stringType, err) + } + return reflect.ValueOf(float32(key)), nil + + case keyType.Kind() == reflect.Float64: + key, err := strconv.ParseFloat(string(data), 64) + if err != nil { + return reflect.Value{}, fmt.Errorf("toml: error parsing key of type %s from float: %w", stringType, err) + } + return reflect.ValueOf(float64(key)), nil } return reflect.Value{}, fmt.Errorf("toml: cannot convert map key of type %s to expected type %s", stringType, keyType) } diff --git a/tools/vendor/github.com/polyfloyd/go-errorlint/errorlint/allowed.go b/tools/vendor/github.com/polyfloyd/go-errorlint/errorlint/allowed.go index 791e50fdb9..cf481708ab 100644 --- a/tools/vendor/github.com/polyfloyd/go-errorlint/errorlint/allowed.go +++ b/tools/vendor/github.com/polyfloyd/go-errorlint/errorlint/allowed.go @@ -57,6 +57,8 @@ func setDefaultAllowedErrors() { {Err: "io.ErrUnexpectedEOF", Fun: "io.ReadAtLeast"}, {Err: "io.EOF", Fun: "io.ReadFull"}, {Err: "io.ErrUnexpectedEOF", Fun: "io.ReadFull"}, + // pkg/mime + {Err: "mime.ErrInvalidMediaParameter", Fun: "mime.ParseMediaType"}, // pkg/net/http {Err: "net/http.ErrServerClosed", Fun: "(*net/http.Server).ListenAndServe"}, {Err: "net/http.ErrServerClosed", Fun: "(*net/http.Server).ListenAndServeTLS"}, diff --git a/tools/vendor/github.com/quasilyte/go-ruleguard/internal/xtypes/xtypes.go b/tools/vendor/github.com/quasilyte/go-ruleguard/internal/xtypes/xtypes.go index 4c5c2a2abe..d29573a6fb 100644 --- a/tools/vendor/github.com/quasilyte/go-ruleguard/internal/xtypes/xtypes.go +++ b/tools/vendor/github.com/quasilyte/go-ruleguard/internal/xtypes/xtypes.go @@ -221,6 +221,10 @@ func typeIdentical(x, y types.Type, p *ifacePair) bool { case *typeparams.TypeParam: // nothing to do (x and y being equal is caught in the very beginning of this function) + case *types.Alias: + // an alias type is identical if the type it's an alias of is identical to it. + return typeIdentical(types.Unalias(x), y, p) + case nil: // avoid a crash in case of nil type diff --git a/tools/vendor/github.com/quasilyte/go-ruleguard/ruleguard/irconv/irconv.go b/tools/vendor/github.com/quasilyte/go-ruleguard/ruleguard/irconv/irconv.go index 4eb90d51b2..cc40506af6 100644 --- a/tools/vendor/github.com/quasilyte/go-ruleguard/ruleguard/irconv/irconv.go +++ b/tools/vendor/github.com/quasilyte/go-ruleguard/ruleguard/irconv/irconv.go @@ -618,8 +618,7 @@ func (conv *converter) convertFilterExprImpl(e ast.Expr) ir.FilterExpr { case *ast.UnaryExpr: x := conv.convertFilterExpr(e.X) args := []ir.FilterExpr{x} - switch e.Op { - case token.NOT: + if e.Op == token.NOT { return ir.FilterExpr{Op: ir.FilterNotOp, Args: args} } diff --git a/tools/vendor/github.com/quasilyte/go-ruleguard/ruleguard/quasigo/compile.go b/tools/vendor/github.com/quasilyte/go-ruleguard/ruleguard/quasigo/compile.go index b81fb8f1db..95ca9297ef 100644 --- a/tools/vendor/github.com/quasilyte/go-ruleguard/ruleguard/quasigo/compile.go +++ b/tools/vendor/github.com/quasilyte/go-ruleguard/ruleguard/quasigo/compile.go @@ -605,11 +605,12 @@ func (cl *compiler) compileCall(key funcKey, sig *types.Signature, args []ast.Ex } var op opcode - if sig.Results().Len() == 0 { + switch { + case sig.Results().Len() == 0: op = opVoidCall - } else if typeIsInt(sig.Results().At(0).Type()) { + case typeIsInt(sig.Results().At(0).Type()): op = opIntCall - } else { + default: op = opCall } diff --git a/tools/vendor/github.com/ryancurrah/gomodguard/.golangci.yml b/tools/vendor/github.com/ryancurrah/gomodguard/.golangci.yml index e39b1445ee..5be8aa487b 100644 --- a/tools/vendor/github.com/ryancurrah/gomodguard/.golangci.yml +++ b/tools/vendor/github.com/ryancurrah/gomodguard/.golangci.yml @@ -3,16 +3,6 @@ linters: enable-all: true disable: - lll - - golint - - deadcode - - maligned - - scopelint - - nosnakecase - - exhaustivestruct - - ifshort - - varcheck - - structcheck - - interfacer - gomodguard - gochecknoglobals - paralleltest @@ -26,7 +16,8 @@ linters: - gofumpt - nonamedreturns - cyclop - - goerr113 + - err113 - perfsprint - tagliatelle - wrapcheck + - mnd diff --git a/tools/vendor/github.com/ryancurrah/gomodguard/.goreleaser.yml b/tools/vendor/github.com/ryancurrah/gomodguard/.goreleaser.yml index f3675a9c27..5a41de8933 100644 --- a/tools/vendor/github.com/ryancurrah/gomodguard/.goreleaser.yml +++ b/tools/vendor/github.com/ryancurrah/gomodguard/.goreleaser.yml @@ -1,3 +1,4 @@ +version: 2 builds: - main: ./cmd/gomodguard/main.go env: @@ -9,6 +10,14 @@ archives: {{- if eq .Arch "amd64" }}x86_64 {{- else if eq .Arch "386" }}i386 {{- else }}{{ .Arch }}{{ end }} + wrap_in_directory: true + format_overrides: + - goos: windows + format: zip + - goos: darwin + format: tar.xz + - goos: linux + format: tar.xz checksum: name_template: 'checksums.txt' dockers: diff --git a/tools/vendor/github.com/ryancurrah/gomodguard/Makefile b/tools/vendor/github.com/ryancurrah/gomodguard/Makefile index a44b707a66..b02bd58096 100644 --- a/tools/vendor/github.com/ryancurrah/gomodguard/Makefile +++ b/tools/vendor/github.com/ryancurrah/gomodguard/Makefile @@ -26,11 +26,11 @@ dockerrun: dockerbuild .PHONY: snapshot snapshot: - goreleaser --rm-dist --snapshot + goreleaser --clean --snapshot .PHONY: release release: - goreleaser --rm-dist + goreleaser --clean .PHONY: clean clean: @@ -43,4 +43,4 @@ install-tools-mac: .PHONY: install-go-tools install-go-tools: - go install -v github.com/t-yuki/gocover-cobertura + go install -v github.com/t-yuki/gocover-cobertura@latest diff --git a/tools/vendor/github.com/ryancurrah/gomodguard/processor.go b/tools/vendor/github.com/ryancurrah/gomodguard/processor.go index 970dbc4574..f3f42a5bec 100644 --- a/tools/vendor/github.com/ryancurrah/gomodguard/processor.go +++ b/tools/vendor/github.com/ryancurrah/gomodguard/processor.go @@ -265,7 +265,7 @@ func loadGoModFile() ([]byte, error) { return os.ReadFile(goModFilename) } - if goEnv["GOMOD"] == "/dev/null" { + if goEnv["GOMOD"] == "/dev/null" || goEnv["GOMOD"] == "NUL" { return nil, errors.New("current working directory must have a go.mod file") } diff --git a/tools/vendor/github.com/ryancurrah/gomodguard/tools.go b/tools/vendor/github.com/ryancurrah/gomodguard/tools.go deleted file mode 100644 index d56bcc7470..0000000000 --- a/tools/vendor/github.com/ryancurrah/gomodguard/tools.go +++ /dev/null @@ -1,5 +0,0 @@ -//go:build tools - -package gomodguard - -import _ "github.com/t-yuki/gocover-cobertura" diff --git a/tools/vendor/github.com/sashamelentyev/usestdlibvars/pkg/analyzer/analyzer.go b/tools/vendor/github.com/sashamelentyev/usestdlibvars/pkg/analyzer/analyzer.go index 581bed2d4e..09f7c82405 100644 --- a/tools/vendor/github.com/sashamelentyev/usestdlibvars/pkg/analyzer/analyzer.go +++ b/tools/vendor/github.com/sashamelentyev/usestdlibvars/pkg/analyzer/analyzer.go @@ -117,8 +117,9 @@ func run(pass *analysis.Pass) (interface{}, error) { case *ast.BinaryExpr: switch n.Op { - case token.LSS, token.GTR, token.LEQ, token.GEQ: + case token.LSS, token.GTR, token.LEQ, token.GEQ, token.QUO, token.ADD, token.SUB, token.MUL: return + default: } x, ok := n.X.(*ast.SelectorExpr) @@ -138,7 +139,6 @@ func run(pass *analysis.Pass) (interface{}, error) { if ok { switchStmt(pass, x, n.Body.List) } - } }) diff --git a/tools/vendor/github.com/securego/gosec/v2/.gitignore b/tools/vendor/github.com/securego/gosec/v2/.gitignore index f6c8065b44..45460260f2 100644 --- a/tools/vendor/github.com/securego/gosec/v2/.gitignore +++ b/tools/vendor/github.com/securego/gosec/v2/.gitignore @@ -37,3 +37,4 @@ _testmain.go # SBOMs generated during CI /bom.json +1 \ No newline at end of file diff --git a/tools/vendor/github.com/securego/gosec/v2/.golangci.yml b/tools/vendor/github.com/securego/gosec/v2/.golangci.yml index d591dc2493..c63a7cc5ad 100644 --- a/tools/vendor/github.com/securego/gosec/v2/.golangci.yml +++ b/tools/vendor/github.com/securego/gosec/v2/.golangci.yml @@ -2,11 +2,11 @@ linters: enable: - asciicheck - bodyclose + - copyloopvar - dogsled - durationcheck - errcheck - errorlint - - exportloopref - gci - ginkgolinter - gochecknoinits @@ -18,7 +18,6 @@ linters: - govet - importas - ineffassign - - megacheck - misspell - nakedret - nolintlint @@ -36,6 +35,11 @@ linters-settings: - standard - default - prefix(github.com/securego) + staticcheck: + checks: + - all + - '-SA1019' + revive: rules: - name: dot-imports diff --git a/tools/vendor/github.com/securego/gosec/v2/Makefile b/tools/vendor/github.com/securego/gosec/v2/Makefile index 4f6cce7651..bcfda6c1ce 100644 --- a/tools/vendor/github.com/securego/gosec/v2/Makefile +++ b/tools/vendor/github.com/securego/gosec/v2/Makefile @@ -17,7 +17,7 @@ GOSEC ?= $(GOBIN)/gosec GINKGO ?= $(GOBIN)/ginkgo GO_MINOR_VERSION = $(shell $(GO) version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f2) GOVULN_MIN_VERSION = 17 -GO_VERSION = 1.22 +GO_VERSION = 1.23 default: $(MAKE) build @@ -68,7 +68,7 @@ build-race: go build -race -o $(BIN) ./cmd/gosec/ clean: - rm -rf build vendor dist coverage.txt + rm -rf build vendor dist coverage.out rm -f release image $(BIN) release: @@ -92,4 +92,7 @@ image-push: image tlsconfig: go generate ./... -.PHONY: test build clean release image image-push tlsconfig +perf-diff: + ./perf-diff.sh + +.PHONY: test build clean release image image-push tlsconfig perf-diff diff --git a/tools/vendor/github.com/securego/gosec/v2/README.md b/tools/vendor/github.com/securego/gosec/v2/README.md index 946536a944..7b8b526a83 100644 --- a/tools/vendor/github.com/securego/gosec/v2/README.md +++ b/tools/vendor/github.com/securego/gosec/v2/README.md @@ -139,6 +139,7 @@ directory you can supply `./...` as the input argument. - G112: Potential slowloris attack - G113: Usage of Rat.SetString in math/big with an overflow (CVE-2022-23772) - G114: Use of net/http serve function that has no support for setting timeouts +- G115: Potential integer overflow when converting between integer types - G201: SQL query construction using format string - G202: SQL query construction using string concatenation - G203: Use of unescaped data in HTML templates @@ -150,15 +151,20 @@ directory you can supply `./...` as the input argument. - G305: File traversal when extracting zip/tar archive - G306: Poor file permissions used when writing to a new file - G307: Poor file permissions used when creating a file with os.Create -- G401: Detect the usage of DES, RC4, MD5 or SHA1 +- G401: Detect the usage of MD5 or SHA1 - G402: Look for bad TLS connection settings - G403: Ensure minimum RSA key length of 2048 bits - G404: Insecure random number source (rand) +- G405: Detect the usage of DES or RC4 +- G406: Detect the usage of MD4 or RIPEMD160 +- G407: Detect the usage of hardcoded Initialization Vector(IV)/Nonce - G501: Import blocklist: crypto/md5 - G502: Import blocklist: crypto/des - G503: Import blocklist: crypto/rc4 - G504: Import blocklist: net/http/cgi - G505: Import blocklist: crypto/sha1 +- G506: Import blocklist: golang.org/x/crypto/md4 +- G507: Import blocklist: golang.org/x/crypto/ripemd160 - G601: Implicit memory aliasing of items from a range statement (only for Go 1.21 or lower) - G602: Slice access out of bounds @@ -269,6 +275,19 @@ gosec can ignore generated go files with default generated code comment. gosec -exclude-generated ./... ``` +### Auto fixing vulnerabilities +gosec can suggest fixes based on AI recommendation. It will call an AI API to receive a suggestion for a security finding. + +You can enable this feature by providing the following command line arguments: +- `ai-api-provider`: the name of the AI API provider, currently only `gemini`is supported. +- `ai-api-key` or set the environment variable `GOSEC_AI_API_KEY`: the key to access the AI API, +For gemini, you can create an API key following [these instructions](https://ai.google.dev/gemini-api/docs/api-key). +- `ai-endpoint`: the endpoint of the AI provider, this is optional argument. + + +```bash +gosec -ai-api-provider="gemini" -ai-api-key="your_key" ./... +``` ### Annotating code @@ -354,7 +373,7 @@ file. The output format is controlled by the `-fmt` flag, and the output file is $ gosec -fmt=json -out=results.json *.go ``` -Results will be reported to stdout as well as to the provided output file by `-stdout` flag. The `-verbose` flag overrides the +Results will be reported to stdout as well as to the provided output file by `-stdout` flag. The `-verbose` flag overrides the output format when stdout the results while saving them in the output file ```bash # Write output in json format to results.json as well as stdout @@ -413,14 +432,14 @@ git push origin v1.0.0 The GitHub [release workflow](.github/workflows/release.yml) triggers immediately after the tag is pushed upstream. This flow will release the binaries using the [goreleaser](https://goreleaser.com/actions/) action and then it will build and publish the docker image into Docker Hub. -The released artifacts are signed using [cosign](https://docs.sigstore.dev/). You can use the public key from [cosign.pub](cosign.pub) +The released artifacts are signed using [cosign](https://docs.sigstore.dev/). You can use the public key from [cosign.pub](cosign.pub) file to verify the signature of docker image and binaries files. The docker image signature can be verified with the following command: ``` cosign verify --key cosign.pub securego/gosec: ``` - + The binary files signature can be verified with the following command: ``` cosign verify-blob --key cosign.pub --signature gosec__darwin_amd64.tar.gz.sig gosec__darwin_amd64.tar.gz diff --git a/tools/vendor/github.com/securego/gosec/v2/action.yml b/tools/vendor/github.com/securego/gosec/v2/action.yml index b1a7767052..d4bc351c86 100644 --- a/tools/vendor/github.com/securego/gosec/v2/action.yml +++ b/tools/vendor/github.com/securego/gosec/v2/action.yml @@ -10,7 +10,7 @@ inputs: runs: using: 'docker' - image: 'docker://securego/gosec:2.20.0' + image: 'docker://securego/gosec:2.21.1' args: - ${{ inputs.args }} diff --git a/tools/vendor/github.com/securego/gosec/v2/analyzer.go b/tools/vendor/github.com/securego/gosec/v2/analyzer.go index f7dd895ed2..bfa7e19406 100644 --- a/tools/vendor/github.com/securego/gosec/v2/analyzer.go +++ b/tools/vendor/github.com/securego/gosec/v2/analyzer.go @@ -122,7 +122,7 @@ func (i ignores) get(file string, line string) map[string][]issue.SuppressionInf start, end := i.parseLine(line) if is, ok := i[file]; ok { for _, i := range is { - if start <= i.start && end >= i.end { + if i.start <= start && i.end >= end || start <= i.start && end >= i.end { return i.suppressions } } @@ -182,7 +182,7 @@ type Analyzer struct { showIgnored bool trackSuppressions bool concurrency int - analyzerList []*analysis.Analyzer + analyzerSet *analyzers.AnalyzerSet mu sync.Mutex } @@ -213,7 +213,7 @@ func NewAnalyzer(conf Config, tests bool, excludeGenerated bool, trackSuppressio concurrency: concurrency, excludeGenerated: excludeGenerated, trackSuppressions: trackSuppressions, - analyzerList: analyzers.BuildDefaultAnalyzers(), + analyzerSet: analyzers.NewAnalyzerSet(), } } @@ -236,6 +236,15 @@ func (gosec *Analyzer) LoadRules(ruleDefinitions map[string]RuleBuilder, ruleSup } } +// LoadAnalyzers instantiates all the analyzers to be used when analyzing source +// packages +func (gosec *Analyzer) LoadAnalyzers(analyzerDefinitions map[string]analyzers.AnalyzerDefinition, analyzerSuppressed map[string]bool) { + for id, def := range analyzerDefinitions { + r := def.Create(def.ID, def.Description) + gosec.analyzerSet.Register(r, analyzerSuppressed[id]) + } +} + // Process kicks off the analysis process for a given package func (gosec *Analyzer) Process(buildTags []string, packagePaths ...string) error { config := &packages.Config{ @@ -390,7 +399,6 @@ func (gosec *Analyzer) CheckRules(pkg *packages.Package) { gosec.context.PkgFiles = pkg.Syntax gosec.context.Imports = NewImportTracker() gosec.context.PassedValues = make(map[string]interface{}) - gosec.context.Ignores = newIgnores() gosec.updateIgnores() ast.Walk(gosec, file) gosec.stats.NumFiles++ @@ -416,7 +424,7 @@ func (gosec *Analyzer) CheckAnalyzers(pkg *packages.Package) { generatedFiles := gosec.generatedFiles(pkg) - for _, analyzer := range gosec.analyzerList { + for _, analyzer := range gosec.analyzerSet.Analyzers { pass := &analysis.Pass{ Analyzer: analyzer, Fset: pkg.Fset, @@ -667,7 +675,7 @@ func (gosec *Analyzer) getSuppressionsAtLineInFile(file string, line string, id suppressions := append(generalSuppressions, ruleSuppressions...) // Track external suppressions of this rule. - if gosec.ruleset.IsRuleSuppressed(id) { + if gosec.ruleset.IsRuleSuppressed(id) || gosec.analyzerSet.IsSuppressed(id) { ignored = true suppressions = append(suppressions, issue.SuppressionInfo{ Kind: "external", @@ -706,4 +714,5 @@ func (gosec *Analyzer) Reset() { gosec.issues = make([]*issue.Issue, 0, 16) gosec.stats = &Metrics{} gosec.ruleset = NewRuleSet() + gosec.analyzerSet = analyzers.NewAnalyzerSet() } diff --git a/tools/vendor/github.com/securego/gosec/v2/analyzers/analyzers_set.go b/tools/vendor/github.com/securego/gosec/v2/analyzers/analyzers_set.go new file mode 100644 index 0000000000..e2fe51c926 --- /dev/null +++ b/tools/vendor/github.com/securego/gosec/v2/analyzers/analyzers_set.go @@ -0,0 +1,38 @@ +// (c) Copyright gosec's authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package analyzers + +import "golang.org/x/tools/go/analysis" + +type AnalyzerSet struct { + Analyzers []*analysis.Analyzer + AnalyzerSuppressedMap map[string]bool +} + +// NewAnalyzerSet constructs a new AnalyzerSet +func NewAnalyzerSet() *AnalyzerSet { + return &AnalyzerSet{nil, make(map[string]bool)} +} + +// Register adds a trigger for the supplied analyzer +func (a *AnalyzerSet) Register(analyzer *analysis.Analyzer, isSuppressed bool) { + a.Analyzers = append(a.Analyzers, analyzer) + a.AnalyzerSuppressedMap[analyzer.Name] = isSuppressed +} + +// IsSuppressed will return whether the Analyzer is suppressed. +func (a *AnalyzerSet) IsSuppressed(ruleID string) bool { + return a.AnalyzerSuppressedMap[ruleID] +} diff --git a/tools/vendor/github.com/securego/gosec/v2/analyzers/analyzerslist.go b/tools/vendor/github.com/securego/gosec/v2/analyzers/analyzerslist.go new file mode 100644 index 0000000000..f2157442f5 --- /dev/null +++ b/tools/vendor/github.com/securego/gosec/v2/analyzers/analyzerslist.go @@ -0,0 +1,95 @@ +// (c) Copyright gosec's authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package analyzers + +import ( + "golang.org/x/tools/go/analysis" +) + +// AnalyzerDefinition contains the description of an analyzer and a mechanism to +// create it. +type AnalyzerDefinition struct { + ID string + Description string + Create AnalyzerBuilder +} + +// AnalyzerBuilder is used to register an analyzer definition with the analyzer +type AnalyzerBuilder func(id string, description string) *analysis.Analyzer + +// AnalyzerList contains a mapping of analyzer ID's to analyzer definitions and a mapping +// of analyzer ID's to whether analyzers are suppressed. +type AnalyzerList struct { + Analyzers map[string]AnalyzerDefinition + AnalyzerSuppressed map[string]bool +} + +// AnalyzersInfo returns all the create methods and the analyzer suppressed map for a +// given list +func (al *AnalyzerList) AnalyzersInfo() (map[string]AnalyzerDefinition, map[string]bool) { + builders := make(map[string]AnalyzerDefinition) + for _, def := range al.Analyzers { + builders[def.ID] = def + } + return builders, al.AnalyzerSuppressed +} + +// AnalyzerFilter can be used to include or exclude an analyzer depending on the return +// value of the function +type AnalyzerFilter func(string) bool + +// NewAnalyzerFilter is a closure that will include/exclude the analyzer ID's based on +// the supplied boolean value. +func NewAnalyzerFilter(action bool, analyzerIDs ...string) AnalyzerFilter { + analyzerlist := make(map[string]bool) + for _, analyzer := range analyzerIDs { + analyzerlist[analyzer] = true + } + return func(analyzer string) bool { + if _, found := analyzerlist[analyzer]; found { + return action + } + return !action + } +} + +var defaultAnalyzers = []AnalyzerDefinition{ + {"G115", "Type conversion which leads to integer overflow", newConversionOverflowAnalyzer}, + {"G602", "Possible slice bounds out of range", newSliceBoundsAnalyzer}, + {"G407", "Use of hardcoded IV/nonce for encryption", newHardCodedNonce}, +} + +// Generate the list of analyzers to use +func Generate(trackSuppressions bool, filters ...AnalyzerFilter) *AnalyzerList { + analyzerMap := make(map[string]AnalyzerDefinition) + analyzerSuppressedMap := make(map[string]bool) + + for _, analyzer := range defaultAnalyzers { + analyzerSuppressedMap[analyzer.ID] = false + addToAnalyzerList := true + for _, filter := range filters { + if filter(analyzer.ID) { + analyzerSuppressedMap[analyzer.ID] = true + if !trackSuppressions { + addToAnalyzerList = false + } + } + } + if addToAnalyzerList { + analyzerMap[analyzer.ID] = analyzer + } + } + return &AnalyzerList{Analyzers: analyzerMap, AnalyzerSuppressed: analyzerSuppressedMap} +} diff --git a/tools/vendor/github.com/securego/gosec/v2/analyzers/conversion_overflow.go b/tools/vendor/github.com/securego/gosec/v2/analyzers/conversion_overflow.go new file mode 100644 index 0000000000..3ef4825af5 --- /dev/null +++ b/tools/vendor/github.com/securego/gosec/v2/analyzers/conversion_overflow.go @@ -0,0 +1,526 @@ +// (c) Copyright gosec's authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package analyzers + +import ( + "fmt" + "go/token" + "math" + "regexp" + "strconv" + "strings" + + "golang.org/x/exp/constraints" + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/buildssa" + "golang.org/x/tools/go/ssa" + + "github.com/securego/gosec/v2/issue" +) + +type integer struct { + signed bool + size int + min int + max uint +} + +type rangeResult struct { + minValue int + maxValue uint + explixitPositiveVals []uint + explicitNegativeVals []int + isRangeCheck bool + convertFound bool +} + +type branchResults struct { + minValue *int + maxValue *uint + explixitPositiveVals []uint + explicitNegativeVals []int + convertFound bool +} + +func newConversionOverflowAnalyzer(id string, description string) *analysis.Analyzer { + return &analysis.Analyzer{ + Name: id, + Doc: description, + Run: runConversionOverflow, + Requires: []*analysis.Analyzer{buildssa.Analyzer}, + } +} + +func runConversionOverflow(pass *analysis.Pass) (interface{}, error) { + ssaResult, err := getSSAResult(pass) + if err != nil { + return nil, fmt.Errorf("building ssa representation: %w", err) + } + + issues := []*issue.Issue{} + for _, mcall := range ssaResult.SSA.SrcFuncs { + for _, block := range mcall.DomPreorder() { + for _, instr := range block.Instrs { + switch instr := instr.(type) { + case *ssa.Convert: + src := instr.X.Type().Underlying().String() + dst := instr.Type().Underlying().String() + if isIntOverflow(src, dst) { + if isSafeConversion(instr) { + continue + } + issue := newIssue(pass.Analyzer.Name, + fmt.Sprintf("integer overflow conversion %s -> %s", src, dst), + pass.Fset, + instr.Pos(), + issue.High, + issue.Medium, + ) + issues = append(issues, issue) + } + } + } + } + } + + if len(issues) > 0 { + return issues, nil + } + return nil, nil +} + +func isIntOverflow(src string, dst string) bool { + srcInt, err := parseIntType(src) + if err != nil { + return false + } + + dstInt, err := parseIntType(dst) + if err != nil { + return false + } + + return srcInt.min < dstInt.min || srcInt.max > dstInt.max +} + +func parseIntType(intType string) (integer, error) { + re := regexp.MustCompile(`^(?Pu?int)(?P\d{1,2})?$`) + matches := re.FindStringSubmatch(intType) + if matches == nil { + return integer{}, fmt.Errorf("no integer type match found for %s", intType) + } + + it := matches[re.SubexpIndex("type")] + is := matches[re.SubexpIndex("size")] + + signed := it == "int" + + // use default system int type in case size is not present in the type. + intSize := strconv.IntSize + if is != "" { + var err error + intSize, err = strconv.Atoi(is) + if err != nil { + return integer{}, fmt.Errorf("failed to parse the integer type size: %w", err) + } + } + + if intSize != 8 && intSize != 16 && intSize != 32 && intSize != 64 && is != "" { + return integer{}, fmt.Errorf("invalid bit size: %d", intSize) + } + + var min int + var max uint + + if signed { + shiftAmount := intSize - 1 + + // Perform a bounds check. + if shiftAmount < 0 { + return integer{}, fmt.Errorf("invalid shift amount: %d", shiftAmount) + } + + max = (1 << uint(shiftAmount)) - 1 + min = -1 << (intSize - 1) + + } else { + max = (1 << uint(intSize)) - 1 + min = 0 + } + + return integer{ + signed: signed, + size: intSize, + min: min, + max: max, + }, nil +} + +func isSafeConversion(instr *ssa.Convert) bool { + dstType := instr.Type().Underlying().String() + + // Check for constant conversions. + if constVal, ok := instr.X.(*ssa.Const); ok { + if isConstantInRange(constVal, dstType) { + return true + } + } + + // Check for string to integer conversions with specified bit size. + if isStringToIntConversion(instr, dstType) { + return true + } + + // Check for explicit range checks. + if hasExplicitRangeCheck(instr, dstType) { + return true + } + + return false +} + +func isConstantInRange(constVal *ssa.Const, dstType string) bool { + value, err := strconv.ParseInt(constVal.Value.String(), 10, 64) + if err != nil { + return false + } + + dstInt, err := parseIntType(dstType) + if err != nil { + return false + } + + if dstInt.signed { + return value >= -(1<<(dstInt.size-1)) && value <= (1<<(dstInt.size-1))-1 + } + return value >= 0 && value <= (1< dstInt.min && maxValue < dstInt.max { + return true + } + + visitedIfs := make(map[*ssa.If]bool) + for _, block := range instr.Parent().Blocks { + for _, blockInstr := range block.Instrs { + switch v := blockInstr.(type) { + case *ssa.If: + result := getResultRange(v, instr, visitedIfs) + if result.isRangeCheck { + minValue = max(minValue, &result.minValue) + maxValue = min(maxValue, &result.maxValue) + explicitPositiveVals = append(explicitPositiveVals, result.explixitPositiveVals...) + explicitNegativeVals = append(explicitNegativeVals, result.explicitNegativeVals...) + } + case *ssa.Call: + // These function return an int of a guaranteed size. + if v != instr.X { + continue + } + if fn, isBuiltin := v.Call.Value.(*ssa.Builtin); isBuiltin { + switch fn.Name() { + case "len", "cap": + minValue = 0 + } + } + } + + if explicitValsInRange(explicitPositiveVals, explicitNegativeVals, dstInt) { + return true + } else if minValue >= dstInt.min && maxValue <= dstInt.max { + return true + } + } + } + return false +} + +// getResultRange is a recursive function that walks the branches of the if statement to find the range of the variable. +func getResultRange(ifInstr *ssa.If, instr *ssa.Convert, visitedIfs map[*ssa.If]bool) rangeResult { + if visitedIfs[ifInstr] { + return rangeResult{minValue: math.MinInt, maxValue: math.MaxUint} + } + visitedIfs[ifInstr] = true + + cond := ifInstr.Cond + binOp, ok := cond.(*ssa.BinOp) + if !ok || !isRangeCheck(binOp, instr.X) { + return rangeResult{minValue: math.MinInt, maxValue: math.MaxUint} + } + + result := rangeResult{ + minValue: math.MinInt, + maxValue: math.MaxUint, + isRangeCheck: true, + } + + thenBounds := walkBranchForConvert(ifInstr.Block().Succs[0], instr, visitedIfs) + elseBounds := walkBranchForConvert(ifInstr.Block().Succs[1], instr, visitedIfs) + + updateResultFromBinOp(&result, binOp, instr, thenBounds.convertFound) + + if thenBounds.convertFound { + result.convertFound = true + result.minValue = max(result.minValue, thenBounds.minValue) + result.maxValue = min(result.maxValue, thenBounds.maxValue) + result.explixitPositiveVals = append(result.explixitPositiveVals, thenBounds.explixitPositiveVals...) + result.explicitNegativeVals = append(result.explicitNegativeVals, thenBounds.explicitNegativeVals...) + } else if elseBounds.convertFound { + result.convertFound = true + result.minValue = max(result.minValue, elseBounds.minValue) + result.maxValue = min(result.maxValue, elseBounds.maxValue) + result.explixitPositiveVals = append(result.explixitPositiveVals, elseBounds.explixitPositiveVals...) + result.explicitNegativeVals = append(result.explicitNegativeVals, elseBounds.explicitNegativeVals...) + } + + return result +} + +// updateResultFromBinOp updates the rangeResult based on the BinOp instruction and the location of the Convert instruction. +func updateResultFromBinOp(result *rangeResult, binOp *ssa.BinOp, instr *ssa.Convert, successPathConvert bool) { + x, y := binOp.X, binOp.Y + operandsFlipped := false + + compareVal, op := getRealValueFromOperation(instr.X) + if x != compareVal { + y, operandsFlipped = x, true + } + + constVal, ok := y.(*ssa.Const) + if !ok { + return + } + + switch binOp.Op { + case token.LEQ, token.LSS: + updateMinMaxForLessOrEqual(result, constVal, binOp.Op, operandsFlipped, successPathConvert) + case token.GEQ, token.GTR: + updateMinMaxForGreaterOrEqual(result, constVal, binOp.Op, operandsFlipped, successPathConvert) + case token.EQL: + if !successPathConvert { + break + } + + // Determine if the constant value is positive or negative. + if strings.Contains(constVal.String(), "-") { + result.explicitNegativeVals = append(result.explicitNegativeVals, int(constVal.Int64())) + } else { + result.explixitPositiveVals = append(result.explixitPositiveVals, uint(constVal.Uint64())) + } + + case token.NEQ: + if successPathConvert { + break + } + + // Determine if the constant value is positive or negative. + if strings.Contains(constVal.String(), "-") { + result.explicitNegativeVals = append(result.explicitNegativeVals, int(constVal.Int64())) + } else { + result.explixitPositiveVals = append(result.explixitPositiveVals, uint(constVal.Uint64())) + } + } + + if op == "neg" { + min := result.minValue + max := result.maxValue + + if min >= 0 { + result.maxValue = uint(min) + } + if max <= math.MaxInt { + result.minValue = int(max) //nolint:gosec + } + } +} + +func updateMinMaxForLessOrEqual(result *rangeResult, constVal *ssa.Const, op token.Token, operandsFlipped bool, successPathConvert bool) { + // If the success path has a conversion and the operands are not flipped, then the constant value is the maximum value. + if successPathConvert && !operandsFlipped { + result.maxValue = uint(constVal.Uint64()) + if op == token.LEQ { + result.maxValue-- + } + } else { + result.minValue = int(constVal.Int64()) + if op == token.GTR { + result.minValue++ + } + } +} + +func updateMinMaxForGreaterOrEqual(result *rangeResult, constVal *ssa.Const, op token.Token, operandsFlipped bool, successPathConvert bool) { + // If the success path has a conversion and the operands are not flipped, then the constant value is the minimum value. + if successPathConvert && !operandsFlipped { + result.minValue = int(constVal.Int64()) + if op == token.GEQ { + result.minValue++ + } + } else { + result.maxValue = uint(constVal.Uint64()) + if op == token.LSS { + result.maxValue-- + } + } +} + +// walkBranchForConvert walks the branch of the if statement to find the range of the variable and where the conversion is. +func walkBranchForConvert(block *ssa.BasicBlock, instr *ssa.Convert, visitedIfs map[*ssa.If]bool) branchResults { + bounds := branchResults{} + + for _, blockInstr := range block.Instrs { + switch v := blockInstr.(type) { + case *ssa.If: + result := getResultRange(v, instr, visitedIfs) + bounds.convertFound = bounds.convertFound || result.convertFound + + if result.isRangeCheck { + bounds.minValue = toPtr(max(result.minValue, bounds.minValue)) + bounds.maxValue = toPtr(min(result.maxValue, bounds.maxValue)) + } + case *ssa.Call: + if v == instr.X { + if fn, isBuiltin := v.Call.Value.(*ssa.Builtin); isBuiltin && (fn.Name() == "len" || fn.Name() == "cap") { + bounds.minValue = toPtr(0) + } + } + case *ssa.Convert: + if v == instr { + bounds.convertFound = true + return bounds + } + } + } + + return bounds +} + +func isRangeCheck(v ssa.Value, x ssa.Value) bool { + compareVal, _ := getRealValueFromOperation(x) + + switch op := v.(type) { + case *ssa.BinOp: + switch op.Op { + case token.LSS, token.LEQ, token.GTR, token.GEQ, + token.EQL, token.NEQ: + return op.X == compareVal || op.Y == compareVal + } + } + return false +} + +func getRealValueFromOperation(v ssa.Value) (ssa.Value, string) { + switch v := v.(type) { + case *ssa.UnOp: + if v.Op == token.SUB { + return v.X, "neg" + } + } + return v, "" +} + +func explicitValsInRange(explicitPosVals []uint, explicitNegVals []int, dstInt integer) bool { + if len(explicitPosVals) == 0 && len(explicitNegVals) == 0 { + return false + } + + for _, val := range explicitPosVals { + if val > dstInt.max { + return false + } + } + + for _, val := range explicitNegVals { + if val < dstInt.min { + return false + } + } + + return true +} + +func min[T constraints.Integer](a T, b *T) T { + if b == nil { + return a + } + if a < *b { + return a + } + return *b +} + +func max[T constraints.Integer](a T, b *T) T { + if b == nil { + return a + } + if a > *b { + return a + } + return *b +} + +func toPtr[T any](a T) *T { + return &a +} diff --git a/tools/vendor/github.com/securego/gosec/v2/analyzers/hardcodedNonce.go b/tools/vendor/github.com/securego/gosec/v2/analyzers/hardcodedNonce.go new file mode 100644 index 0000000000..b07363388d --- /dev/null +++ b/tools/vendor/github.com/securego/gosec/v2/analyzers/hardcodedNonce.go @@ -0,0 +1,246 @@ +// (c) Copyright gosec's authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package analyzers + +import ( + "errors" + "fmt" + "go/token" + "strings" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/buildssa" + "golang.org/x/tools/go/ssa" + + "github.com/securego/gosec/v2/issue" +) + +const defaultIssueDescription = "Use of hardcoded IV/nonce for encryption" + +func newHardCodedNonce(id string, description string) *analysis.Analyzer { + return &analysis.Analyzer{ + Name: id, + Doc: description, + Run: runHardCodedNonce, + Requires: []*analysis.Analyzer{buildssa.Analyzer}, + } +} + +func runHardCodedNonce(pass *analysis.Pass) (interface{}, error) { + ssaResult, err := getSSAResult(pass) + if err != nil { + return nil, fmt.Errorf("building ssa representation: %w", err) + } + + // Holds the function name as key, the number of arguments that the function accepts, and at which index of those accepted arguments is the nonce/IV + // Example "Test" 3, 1 -- means the function "Test" which accepts 3 arguments, and has the nonce arg as second argument + calls := map[string][]int{ + "(crypto/cipher.AEAD).Seal": {4, 1}, + "(crypto/cipher.AEAD).Open": {4, 1}, + "crypto/cipher.NewCBCDecrypter": {2, 1}, + "crypto/cipher.NewCBCEncrypter": {2, 1}, + "crypto/cipher.NewCFBDecrypter": {2, 1}, + "crypto/cipher.NewCFBEncrypter": {2, 1}, + "crypto/cipher.NewCTR": {2, 1}, + "crypto/cipher.NewOFB": {2, 1}, + } + ssaPkgFunctions := ssaResult.SSA.SrcFuncs + args := getArgsFromTrackedFunctions(ssaPkgFunctions, calls) + if args == nil { + return nil, errors.New("no tracked functions found, resulting in no variables to track") + } + var issues []*issue.Issue + for _, arg := range args { + if arg == nil { + continue + } + i, err := raiseIssue(*arg, calls, ssaPkgFunctions, pass, "") + if err != nil { + return issues, fmt.Errorf("raising issue error: %w", err) + } + issues = append(issues, i...) + } + return issues, nil +} + +func raiseIssue(val ssa.Value, funcsToTrack map[string][]int, ssaFuncs []*ssa.Function, + pass *analysis.Pass, issueDescription string, +) ([]*issue.Issue, error) { + if issueDescription == "" { + issueDescription = defaultIssueDescription + } + var err error + var allIssues []*issue.Issue + var issues []*issue.Issue + switch valType := (val).(type) { + case *ssa.Slice: + issueDescription += " by passing hardcoded slice/array" + issues, err = iterateThroughReferrers(val, funcsToTrack, pass.Analyzer.Name, issueDescription, pass.Fset, issue.High) + allIssues = append(allIssues, issues...) + case *ssa.UnOp: + // Check if it's a dereference operation (a.k.a pointer) + if valType.Op == token.MUL { + issueDescription += " by passing pointer which points to hardcoded variable" + issues, err = iterateThroughReferrers(val, funcsToTrack, pass.Analyzer.Name, issueDescription, pass.Fset, issue.Low) + allIssues = append(allIssues, issues...) + } + // When the value assigned to a variable is a function call. + // It goes and check if this function contains call to crypto/rand.Read + // in it's body(Assuming that calling crypto/rand.Read in a function, + // is used for the generation of nonce/iv ) + case *ssa.Call: + if callValue := valType.Call.Value; callValue != nil { + if calledFunction, ok := callValue.(*ssa.Function); ok { + if contains, funcErr := isFuncContainsCryptoRand(calledFunction); !contains && funcErr == nil { + issueDescription += " by passing a value from function which doesn't use crypto/rand" + issues, err = iterateThroughReferrers(val, funcsToTrack, pass.Analyzer.Name, issueDescription, pass.Fset, issue.Medium) + allIssues = append(allIssues, issues...) + } else if funcErr != nil { + err = funcErr + } + } + } + // only checks from strings->[]byte + // might need to add additional types + case *ssa.Convert: + if valType.Type().String() == "[]byte" && valType.X.Type().String() == "string" { + issueDescription += " by passing converted string" + issues, err = iterateThroughReferrers(val, funcsToTrack, pass.Analyzer.Name, issueDescription, pass.Fset, issue.High) + allIssues = append(allIssues, issues...) + } + case *ssa.Parameter: + // arg given to tracked function is wrapped in another function, example: + // func encrypt(..,nonce,...){ + // aesgcm.Seal(nonce) + // } + // save parameter position, by checking the name of the variable used in + // tracked functions and comparing it with the name of the arg + if valType.Parent() != nil { + trackedFunctions := make(map[string][]int) + for index, funcArgs := range valType.Parent().Params { + if funcArgs.Name() == valType.Name() && funcArgs.Type() == valType.Type() { + trackedFunctions[valType.Parent().String()] = []int{len(valType.Parent().Params), index} + } + } + args := getArgsFromTrackedFunctions(ssaFuncs, trackedFunctions) + + issueDescription += " by passing a parameter to a function and" + // recursively backtrack to where the origin of a variable passed to multiple functions is + for _, arg := range args { + if arg == nil { + continue + } + issues, err = raiseIssue(*arg, trackedFunctions, ssaFuncs, pass, issueDescription) + allIssues = append(allIssues, issues...) + } + } + } + return allIssues, err +} + +// iterateThroughReferrers iterates through all places that use the `variable` argument and check if it's used in one of the tracked functions. +func iterateThroughReferrers(variable ssa.Value, funcsToTrack map[string][]int, + analyzerID string, issueDescription string, + fileSet *token.FileSet, issueConfidence issue.Score, +) ([]*issue.Issue, error) { + if funcsToTrack == nil || variable == nil || analyzerID == "" || issueDescription == "" || fileSet == nil { + return nil, errors.New("received a nil object") + } + var gosecIssues []*issue.Issue + refs := variable.Referrers() + if refs == nil { + return gosecIssues, nil + } + // Go trough all functions that use the given arg variable + for _, ref := range *refs { + // Iterate trough the functions we are interested + for trackedFunc := range funcsToTrack { + + // Split the functions we are interested in, by the '.' because we will use the function name to do the comparison + // MIGHT GIVE SOME FALSE POSITIVES THIS WAY + trackedFuncParts := strings.Split(trackedFunc, ".") + trackedFuncPartsName := trackedFuncParts[len(trackedFuncParts)-1] + if strings.Contains(ref.String(), trackedFuncPartsName) { + gosecIssues = append(gosecIssues, newIssue(analyzerID, issueDescription, fileSet, ref.Pos(), issue.High, issueConfidence)) + } + } + } + return gosecIssues, nil +} + +// isFuncContainsCryptoRand checks whether a function contains a call to crypto/rand.Read in it's function body. +func isFuncContainsCryptoRand(funcCall *ssa.Function) (bool, error) { + if funcCall == nil { + return false, errors.New("passed ssa.Function object is nil") + } + for _, block := range funcCall.Blocks { + for _, instr := range block.Instrs { + if call, ok := instr.(*ssa.Call); ok { + if calledFunction, ok := call.Call.Value.(*ssa.Function); ok { + if calledFunction.Pkg != nil && calledFunction.Pkg.Pkg.Path() == "crypto/rand" && calledFunction.Name() == "Read" { + return true, nil + } + } + } + } + } + return false, nil +} + +func addToVarsMap(value ssa.Value, mapToAddTo map[string]*ssa.Value) { + key := value.Name() + value.Type().String() + value.String() + value.Parent().String() + mapToAddTo[key] = &value +} + +func isContainedInMap(value ssa.Value, mapToCheck map[string]*ssa.Value) bool { + key := value.Name() + value.Type().String() + value.String() + value.Parent().String() + _, contained := mapToCheck[key] + return contained +} + +func getArgsFromTrackedFunctions(ssaFuncs []*ssa.Function, trackedFunc map[string][]int) map[string]*ssa.Value { + values := make(map[string]*ssa.Value) + for _, pkgFunc := range ssaFuncs { + for _, funcBlock := range pkgFunc.Blocks { + for _, funcBlocInstr := range funcBlock.Instrs { + iterateTrackedFunctionsAndAddArgs(funcBlocInstr, trackedFunc, values) + } + } + } + return values +} + +func iterateTrackedFunctionsAndAddArgs(funcBlocInstr ssa.Instruction, trackedFunc map[string][]int, values map[string]*ssa.Value) { + if funcCall, ok := (funcBlocInstr).(*ssa.Call); ok { + for trackedFuncName, trackedFuncArgsInfo := range trackedFunc { + // only process functions that have the same number of arguments as the ones we track + if len(funcCall.Call.Args) == trackedFuncArgsInfo[0] { + tmpArg := funcCall.Call.Args[trackedFuncArgsInfo[1]] + // check if the function is called from an object or directly from the package + if funcCall.Call.Method != nil { + if methodFullname := funcCall.Call.Method.FullName(); methodFullname == trackedFuncName { + if !isContainedInMap(tmpArg, values) { + addToVarsMap(tmpArg, values) + } + } + } else if funcCall.Call.Value.String() == trackedFuncName { + if !isContainedInMap(tmpArg, values) { + addToVarsMap(tmpArg, values) + } + } + } + } + } +} diff --git a/tools/vendor/github.com/securego/gosec/v2/analyzers/slice_bounds.go b/tools/vendor/github.com/securego/gosec/v2/analyzers/slice_bounds.go index 08a55eb429..968102f268 100644 --- a/tools/vendor/github.com/securego/gosec/v2/analyzers/slice_bounds.go +++ b/tools/vendor/github.com/securego/gosec/v2/analyzers/slice_bounds.go @@ -118,32 +118,45 @@ func runSliceBounds(pass *analysis.Pass) (interface{}, error) { if i == 1 { bound = invBound(bound) } - for _, instr := range block.Instrs { - if _, ok := issues[instr]; ok { - switch bound { - case lowerUnbounded: - break - case upperUnbounded, unbounded: - delete(issues, instr) - case upperBounded: - switch tinstr := instr.(type) { - case *ssa.Slice: - lower, upper := extractSliceBounds(tinstr) - if isSliceInsideBounds(0, value, lower, upper) { - delete(issues, instr) - } - case *ssa.IndexAddr: - indexValue, err := extractIntValue(tinstr.Index.String()) - if err != nil { - break - } - if isSliceIndexInsideBounds(0, value, indexValue) { - delete(issues, instr) + var processBlock func(block *ssa.BasicBlock, depth int) + processBlock = func(block *ssa.BasicBlock, depth int) { + if depth == maxDepth { + return + } + depth++ + for _, instr := range block.Instrs { + if _, ok := issues[instr]; ok { + switch bound { + case lowerUnbounded: + break + case upperUnbounded, unbounded: + delete(issues, instr) + case upperBounded: + switch tinstr := instr.(type) { + case *ssa.Slice: + lower, upper := extractSliceBounds(tinstr) + if isSliceInsideBounds(0, value, lower, upper) { + delete(issues, instr) + } + case *ssa.IndexAddr: + indexValue, err := extractIntValue(tinstr.Index.String()) + if err != nil { + break + } + if isSliceIndexInsideBounds(0, value, indexValue) { + delete(issues, instr) + } } } + } else if nestedIfInstr, ok := instr.(*ssa.If); ok { + for _, nestedBlock := range nestedIfInstr.Block().Succs { + processBlock(nestedBlock, depth) + } } } } + + processBlock(block, 0) } } diff --git a/tools/vendor/github.com/securego/gosec/v2/analyzers/util.go b/tools/vendor/github.com/securego/gosec/v2/analyzers/util.go index 5941184aa2..e7961a56b7 100644 --- a/tools/vendor/github.com/securego/gosec/v2/analyzers/util.go +++ b/tools/vendor/github.com/securego/gosec/v2/analyzers/util.go @@ -38,7 +38,9 @@ type SSAAnalyzerResult struct { // BuildDefaultAnalyzers returns the default list of analyzers func BuildDefaultAnalyzers() []*analysis.Analyzer { return []*analysis.Analyzer{ + newConversionOverflowAnalyzer("G115", "Type conversion which leads to integer overflow"), newSliceBoundsAnalyzer("G602", "Possible slice bounds out of range"), + newHardCodedNonce("G407", "Use of hardcoded IV/nonce for encryption"), } } diff --git a/tools/vendor/github.com/securego/gosec/v2/cwe/data.go b/tools/vendor/github.com/securego/gosec/v2/cwe/data.go index 79a6b9d231..a9568ba4d5 100644 --- a/tools/vendor/github.com/securego/gosec/v2/cwe/data.go +++ b/tools/vendor/github.com/securego/gosec/v2/cwe/data.go @@ -18,6 +18,31 @@ const ( ) var idWeaknesses = map[string]*Weakness{ + "22": { + ID: "22", + Description: "The software uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the software does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.", + Name: "Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')", + }, + "78": { + ID: "78", + Description: "The software constructs all or part of an OS command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended OS command when it is sent to a downstream component.", + Name: "Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')", + }, + "79": { + ID: "79", + Description: "The software does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.", + Name: "Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')", + }, + "88": { + ID: "88", + Description: "The software constructs a string for a command to executed by a separate component\nin another control sphere, but it does not properly delimit the\nintended arguments, options, or switches within that command string.", + Name: "Improper Neutralization of Argument Delimiters in a Command ('Argument Injection')", + }, + "89": { + ID: "89", + Description: "The software constructs all or part of an SQL command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended SQL command when it is sent to a downstream component.", + Name: "Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')", + }, "118": { ID: "118", Description: "The software does not restrict or incorrectly restricts operations within the boundaries of a resource that is accessed using an index or pointer, such as memory or files.", @@ -33,11 +58,6 @@ var idWeaknesses = map[string]*Weakness{ Description: "The product exposes sensitive information to an actor that is not explicitly authorized to have access to that information.", Name: "Exposure of Sensitive Information to an Unauthorized Actor", }, - "22": { - ID: "22", - Description: "The software uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the software does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.", - Name: "Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')", - }, "242": { ID: "242", Description: "The program calls a function that can never be guaranteed to work safely.", @@ -73,6 +93,11 @@ var idWeaknesses = map[string]*Weakness{ Description: "The use of a broken or risky cryptographic algorithm is an unnecessary risk that may result in the exposure of sensitive information.", Name: "Use of a Broken or Risky Cryptographic Algorithm", }, + "328": { + ID: "328", + Description: "The product uses an algorithm that produces a digest (output value) that does not meet security expectations for a hash function that allows an adversary to reasonably determine the original input (preimage attack), find another input that can produce the same hash (2nd preimage attack), or find multiple inputs that evaluate to the same hash (birthday attack). ", + Name: "Use of Weak Hash", + }, "338": { ID: "338", Description: "The product uses a Pseudo-Random Number Generator (PRNG) in a security context, but the PRNG's algorithm is not cryptographically strong.", @@ -93,40 +118,25 @@ var idWeaknesses = map[string]*Weakness{ Description: "The software does not handle or incorrectly handles a compressed input with a very high compression ratio that produces a large output.", Name: "Improper Handling of Highly Compressed Data (Data Amplification)", }, + "676": { + ID: "676", + Description: "The program invokes a potentially dangerous function that could introduce a vulnerability if it is used incorrectly, but the function can also be used safely.", + Name: "Use of Potentially Dangerous Function", + }, "703": { ID: "703", Description: "The software does not properly anticipate or handle exceptional conditions that rarely occur during normal operation of the software.", Name: "Improper Check or Handling of Exceptional Conditions", }, - "78": { - ID: "78", - Description: "The software constructs all or part of an OS command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended OS command when it is sent to a downstream component.", - Name: "Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')", - }, - "79": { - ID: "79", - Description: "The software does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.", - Name: "Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')", - }, "798": { ID: "798", Description: "The software contains hard-coded credentials, such as a password or cryptographic key, which it uses for its own inbound authentication, outbound communication to external components, or encryption of internal data.", Name: "Use of Hard-coded Credentials", }, - "88": { - ID: "88", - Description: "The software constructs a string for a command to executed by a separate component\nin another control sphere, but it does not properly delimit the\nintended arguments, options, or switches within that command string.", - Name: "Improper Neutralization of Argument Delimiters in a Command ('Argument Injection')", - }, - "89": { - ID: "89", - Description: "The software constructs all or part of an SQL command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended SQL command when it is sent to a downstream component.", - Name: "Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')", - }, - "676": { - ID: "676", - Description: "The program invokes a potentially dangerous function that could introduce a vulnerability if it is used incorrectly, but the function can also be used safely.", - Name: "Use of Potentially Dangerous Function", + "1204": { + ID: "1204", + Description: "The product uses a cryptographic primitive that uses an Initialization Vector (IV), but the product does not generate IVs that are sufficiently unpredictable or unique according to the expected cryptographic requirements for that primitive.", + Name: "Generation of Weak Initialization Vector (IV)", }, } diff --git a/tools/vendor/github.com/securego/gosec/v2/issue/issue.go b/tools/vendor/github.com/securego/gosec/v2/issue/issue.go index 1000b20423..6227db670e 100644 --- a/tools/vendor/github.com/securego/gosec/v2/issue/issue.go +++ b/tools/vendor/github.com/securego/gosec/v2/issue/issue.go @@ -67,6 +67,7 @@ var ruleToCWE = map[string]string{ "G112": "400", "G113": "190", "G114": "676", + "G115": "190", "G201": "89", "G202": "89", "G203": "79", @@ -77,32 +78,38 @@ var ruleToCWE = map[string]string{ "G304": "22", "G305": "22", "G306": "276", - "G401": "326", + "G401": "328", "G402": "295", "G403": "310", "G404": "338", + "G405": "327", + "G406": "328", + "G407": "1204", "G501": "327", "G502": "327", "G503": "327", "G504": "327", "G505": "327", + "G506": "327", + "G507": "327", "G601": "118", "G602": "118", } // Issue is returned by a gosec rule if it discovers an issue with the scanned code. type Issue struct { - Severity Score `json:"severity"` // issue severity (how problematic it is) - Confidence Score `json:"confidence"` // issue confidence (how sure we are we found it) - Cwe *cwe.Weakness `json:"cwe"` // Cwe associated with RuleID - RuleID string `json:"rule_id"` // Human readable explanation - What string `json:"details"` // Human readable explanation - File string `json:"file"` // File name we found it in - Code string `json:"code"` // Impacted code line - Line string `json:"line"` // Line number in file - Col string `json:"column"` // Column number in line - NoSec bool `json:"nosec"` // true if the issue is nosec - Suppressions []SuppressionInfo `json:"suppressions"` // Suppression info of the issue + Severity Score `json:"severity"` // issue severity (how problematic it is) + Confidence Score `json:"confidence"` // issue confidence (how sure we are we found it) + Cwe *cwe.Weakness `json:"cwe"` // Cwe associated with RuleID + RuleID string `json:"rule_id"` // Human readable explanation + What string `json:"details"` // Human readable explanation + File string `json:"file"` // File name we found it in + Code string `json:"code"` // Impacted code line + Line string `json:"line"` // Line number in file + Col string `json:"column"` // Column number in line + NoSec bool `json:"nosec"` // true if the issue is nosec + Suppressions []SuppressionInfo `json:"suppressions"` // Suppression info of the issue + Autofix string `json:"autofix,omitempty"` // Proposed auto fix the issue } // SuppressionInfo object is to record the kind and the justification that used diff --git a/tools/vendor/github.com/securego/gosec/v2/perf-diff.sh b/tools/vendor/github.com/securego/gosec/v2/perf-diff.sh new file mode 100644 index 0000000000..cf3084cbf6 --- /dev/null +++ b/tools/vendor/github.com/securego/gosec/v2/perf-diff.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +BIN="gosec" +BUILD_DIR="/tmp/securego" + +# Scan the current folder and measure the duration. +function scan() { + local scan_cmd=$1 + s=$(date +%s%3N) + $scan_cmd -quiet ./... + e=$(date +%s%3N) + res=$(expr $e - $s) + echo $res +} + +# Build the master reference version. +mkdir -p ${BUILD_DIR} +git clone --quiet https://github.com/securego/gosec.git ${BUILD_DIR} >/dev/null +make -C ${BUILD_DIR} >/dev/null + +# Scan once with the main reference. +duration_master=$(scan "${BUILD_DIR}/${BIN}") +echo "gosec reference time: ${duration_master}ms" + +# Build the current version. +make -C . >/dev/null + +# Scan once with the current version. +duration=$(scan "./${BIN}") +echo "gosec time: ${duration}ms" + +# Compute the difference of the execution time. +diff=$(($duration - $duration_master)) +if [[ diff -lt 0 ]]; then + diff=$(($diff * -1)) +fi +echo "diff: ${diff}ms" +perf=$((100 - ($duration * 100) / $duration_master)) +echo "perf diff: ${perf}%" + +# Fail the build if there is a performance degradation of more than 10%. +if [[ $perf -lt -10 ]]; then + exit 1 +fi diff --git a/tools/vendor/github.com/securego/gosec/v2/rules/blocklist.go b/tools/vendor/github.com/securego/gosec/v2/rules/blocklist.go index 5e03cf7a0c..a4376b19ad 100644 --- a/tools/vendor/github.com/securego/gosec/v2/rules/blocklist.go +++ b/tools/vendor/github.com/securego/gosec/v2/rules/blocklist.go @@ -93,3 +93,17 @@ func NewBlocklistedImportSHA1(id string, conf gosec.Config) (gosec.Rule, []ast.N "crypto/sha1": "Blocklisted import crypto/sha1: weak cryptographic primitive", }) } + +// NewBlocklistedImportMD4 fails if MD4 is imported +func NewBlocklistedImportMD4(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { + return NewBlocklistedImports(id, conf, map[string]string{ + "golang.org/x/crypto/md4": "Blocklisted import golang.org/x/crypto/md4: deprecated and weak cryptographic primitive", + }) +} + +// NewBlocklistedImportRIPEMD160 fails if RIPEMD160 is imported +func NewBlocklistedImportRIPEMD160(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { + return NewBlocklistedImports(id, conf, map[string]string{ + "golang.org/x/crypto/ripemd160": "Blocklisted import golang.org/x/crypto/ripemd160: deprecated and weak cryptographic primitive", + }) +} diff --git a/tools/vendor/github.com/securego/gosec/v2/rules/rulelist.go b/tools/vendor/github.com/securego/gosec/v2/rules/rulelist.go index f9ca4f52c4..13f29f71aa 100644 --- a/tools/vendor/github.com/securego/gosec/v2/rules/rulelist.go +++ b/tools/vendor/github.com/securego/gosec/v2/rules/rulelist.go @@ -94,10 +94,12 @@ func Generate(trackSuppressions bool, filters ...RuleFilter) RuleList { {"G307", "Poor file permissions used when creating a file with os.Create", NewOsCreatePerms}, // crypto - {"G401", "Detect the usage of DES, RC4, MD5 or SHA1", NewUsesWeakCryptography}, + {"G401", "Detect the usage of MD5 or SHA1", NewUsesWeakCryptographyHash}, {"G402", "Look for bad TLS connection settings", NewIntermediateTLSCheck}, {"G403", "Ensure minimum RSA key length of 2048 bits", NewWeakKeyStrength}, {"G404", "Insecure random number source (rand)", NewWeakRandCheck}, + {"G405", "Detect the usage of DES or RC4", NewUsesWeakCryptographyEncryption}, + {"G406", "Detect the usage of deprecated MD4 or RIPEMD160", NewUsesWeakDeprecatedCryptographyHash}, // blocklist {"G501", "Import blocklist: crypto/md5", NewBlocklistedImportMD5}, @@ -105,6 +107,8 @@ func Generate(trackSuppressions bool, filters ...RuleFilter) RuleList { {"G503", "Import blocklist: crypto/rc4", NewBlocklistedImportRC4}, {"G504", "Import blocklist: net/http/cgi", NewBlocklistedImportCGI}, {"G505", "Import blocklist: crypto/sha1", NewBlocklistedImportSHA1}, + {"G506", "Import blocklist: golang.org/x/crypto/md4", NewBlocklistedImportMD4}, + {"G507", "Import blocklist: golang.org/x/crypto/ripemd160", NewBlocklistedImportRIPEMD160}, // memory safety {"G601", "Implicit memory aliasing in RangeStmt", NewImplicitAliasing}, diff --git a/tools/vendor/github.com/securego/gosec/v2/rules/templates.go b/tools/vendor/github.com/securego/gosec/v2/rules/templates.go index 728766f457..3d5f9a977a 100644 --- a/tools/vendor/github.com/securego/gosec/v2/rules/templates.go +++ b/tools/vendor/github.com/securego/gosec/v2/rules/templates.go @@ -45,9 +45,12 @@ func (t *templateCheck) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error // find use of templates where HTML/JS escaping is not being used func NewTemplateCheck(id string, _ gosec.Config) (gosec.Rule, []ast.Node) { calls := gosec.NewCallList() + calls.Add("html/template", "CSS") calls.Add("html/template", "HTML") calls.Add("html/template", "HTMLAttr") calls.Add("html/template", "JS") + calls.Add("html/template", "JSStr") + calls.Add("html/template", "Srcset") calls.Add("html/template", "URL") return &templateCheck{ calls: calls, diff --git a/tools/vendor/github.com/securego/gosec/v2/rules/weakcrypto.go b/tools/vendor/github.com/securego/gosec/v2/rules/weakcrypto.go index 4f2ab11d15..143f67d4e8 100644 --- a/tools/vendor/github.com/securego/gosec/v2/rules/weakcrypto.go +++ b/tools/vendor/github.com/securego/gosec/v2/rules/weakcrypto.go @@ -21,16 +21,16 @@ import ( "github.com/securego/gosec/v2/issue" ) -type usesWeakCryptography struct { +type usesWeakCryptographyEncryption struct { issue.MetaData blocklist map[string][]string } -func (r *usesWeakCryptography) ID() string { +func (r *usesWeakCryptographyEncryption) ID() string { return r.MetaData.ID } -func (r *usesWeakCryptography) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) { +func (r *usesWeakCryptographyEncryption) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) { for pkg, funcs := range r.blocklist { if _, matched := gosec.MatchCallByPackage(n, c, pkg, funcs...); matched { return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil @@ -39,14 +39,12 @@ func (r *usesWeakCryptography) Match(n ast.Node, c *gosec.Context) (*issue.Issue return nil, nil } -// NewUsesWeakCryptography detects uses of des.* md5.* or rc4.* -func NewUsesWeakCryptography(id string, _ gosec.Config) (gosec.Rule, []ast.Node) { +// NewUsesWeakCryptographyEncryption detects uses of des.*, rc4.* +func NewUsesWeakCryptographyEncryption(id string, _ gosec.Config) (gosec.Rule, []ast.Node) { calls := make(map[string][]string) calls["crypto/des"] = []string{"NewCipher", "NewTripleDESCipher"} - calls["crypto/md5"] = []string{"New", "Sum"} - calls["crypto/sha1"] = []string{"New", "Sum"} calls["crypto/rc4"] = []string{"NewCipher"} - rule := &usesWeakCryptography{ + rule := &usesWeakCryptographyEncryption{ blocklist: calls, MetaData: issue.MetaData{ ID: id, diff --git a/tools/vendor/github.com/securego/gosec/v2/rules/weakcryptohash.go b/tools/vendor/github.com/securego/gosec/v2/rules/weakcryptohash.go new file mode 100644 index 0000000000..298555de12 --- /dev/null +++ b/tools/vendor/github.com/securego/gosec/v2/rules/weakcryptohash.go @@ -0,0 +1,55 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package rules + +import ( + "go/ast" + + "github.com/securego/gosec/v2" + "github.com/securego/gosec/v2/issue" +) + +type usesWeakCryptographyHash struct { + issue.MetaData + blocklist map[string][]string +} + +func (r *usesWeakCryptographyHash) ID() string { + return r.MetaData.ID +} + +func (r *usesWeakCryptographyHash) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) { + for pkg, funcs := range r.blocklist { + if _, matched := gosec.MatchCallByPackage(n, c, pkg, funcs...); matched { + return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil + } + } + return nil, nil +} + +// NewUsesWeakCryptographyHash detects uses of md5.*, sha1.* +func NewUsesWeakCryptographyHash(id string, _ gosec.Config) (gosec.Rule, []ast.Node) { + calls := make(map[string][]string) + calls["crypto/md5"] = []string{"New", "Sum"} + calls["crypto/sha1"] = []string{"New", "Sum"} + rule := &usesWeakCryptographyHash{ + blocklist: calls, + MetaData: issue.MetaData{ + ID: id, + Severity: issue.Medium, + Confidence: issue.High, + What: "Use of weak cryptographic primitive", + }, + } + return rule, []ast.Node{(*ast.CallExpr)(nil)} +} diff --git a/tools/vendor/github.com/securego/gosec/v2/rules/weakdepricatedcryptohash.go b/tools/vendor/github.com/securego/gosec/v2/rules/weakdepricatedcryptohash.go new file mode 100644 index 0000000000..68297355c1 --- /dev/null +++ b/tools/vendor/github.com/securego/gosec/v2/rules/weakdepricatedcryptohash.go @@ -0,0 +1,57 @@ +// (c) Copyright 2024 Mercedes-Benz Tech Innovation GmbH +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package rules + +import ( + "go/ast" + + "github.com/securego/gosec/v2" + "github.com/securego/gosec/v2/issue" +) + +type usesWeakDeprecatedCryptographyHash struct { + issue.MetaData + blocklist map[string][]string +} + +func (r *usesWeakDeprecatedCryptographyHash) ID() string { + return r.MetaData.ID +} + +func (r *usesWeakDeprecatedCryptographyHash) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) { + for pkg, funcs := range r.blocklist { + if _, matched := gosec.MatchCallByPackage(n, c, pkg, funcs...); matched { + return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil + } + } + return nil, nil +} + +// NewUsesWeakCryptographyHash detects uses of md4.New, ripemd160.New +func NewUsesWeakDeprecatedCryptographyHash(id string, _ gosec.Config) (gosec.Rule, []ast.Node) { + calls := make(map[string][]string) + calls["golang.org/x/crypto/md4"] = []string{"New"} + calls["golang.org/x/crypto/ripemd160"] = []string{"New"} + rule := &usesWeakDeprecatedCryptographyHash{ + blocklist: calls, + MetaData: issue.MetaData{ + ID: id, + Severity: issue.Medium, + Confidence: issue.High, + What: "Use of deprecated weak cryptographic primitive", + }, + } + return rule, []ast.Node{(*ast.CallExpr)(nil)} +} diff --git a/tools/vendor/github.com/sivchari/tenv/README.md b/tools/vendor/github.com/sivchari/tenv/README.md index c5d0047734..56389e2dd0 100644 --- a/tools/vendor/github.com/sivchari/tenv/README.md +++ b/tools/vendor/github.com/sivchari/tenv/README.md @@ -1,6 +1,6 @@ # tenv -![tenv Gopher](./tenv.png "Gopher") +tenv Gopher [![test_and_lint](https://github.com/sivchari/tenv/actions/workflows/workflows.yml/badge.svg?branch=main)](https://github.com/sivchari/tenv/actions/workflows/workflows.yml) @@ -10,7 +10,7 @@ tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17 ## Instruction ```sh -go install github.com/sivchari/tenv/cmd/tenv +go install github.com/sivchari/tenv/cmd/tenv@latest ``` ## Usage @@ -39,7 +39,7 @@ func helper() { ``` ```console -go vet -vettool=(which tenv) ./... +go vet -vettool=$(which tenv) ./... # a ./main_test.go:11:2: os.Setenv() can be replaced by `t.Setenv()` in TestMain @@ -75,7 +75,7 @@ func helper() { ``` ```console -go vet -vettool=(which tenv) -tenv.all ./... +go vet -vettool=$(which tenv) -tenv.all ./... # a ./main_test.go:11:2: os.Setenv() can be replaced by `t.Setenv()` in TestMain @@ -89,7 +89,7 @@ go vet -vettool=(which tenv) -tenv.all ./... ```yaml - run: name: install tenv - command: go install github.com/sivchari/tenv + command: go install github.com/sivchari/tenv@latest - run: name: run tenv @@ -100,7 +100,7 @@ go vet -vettool=(which tenv) -tenv.all ./... ```yaml - name: install tenv - run: go install github.com/sivchari/tenv + run: go install github.com/sivchari/tenv@latest - name: run tenv run: go vet -vettool=`which tenv` ./... diff --git a/tools/vendor/github.com/sivchari/tenv/tenv.go b/tools/vendor/github.com/sivchari/tenv/tenv.go index fcff98d058..999c5289dc 100644 --- a/tools/vendor/github.com/sivchari/tenv/tenv.go +++ b/tools/vendor/github.com/sivchari/tenv/tenv.go @@ -81,6 +81,8 @@ func checkStmts(pass *analysis.Pass, stmts []ast.Stmt, funcName, argName string) if !checkAssignStmt(pass, stmt, funcName, argName) { continue } + case *ast.ForStmt: + checkForStmt(pass, stmt, funcName, argName) } } } @@ -90,6 +92,7 @@ func checkExprStmt(pass *analysis.Pass, stmt *ast.ExprStmt, funcName, argName st if !ok { return false } + checkArgs(pass, callExpr.Args, funcName, argName) fun, ok := callExpr.Fun.(*ast.SelectorExpr) if !ok { return false @@ -108,6 +111,30 @@ func checkExprStmt(pass *analysis.Pass, stmt *ast.ExprStmt, funcName, argName st return true } +func checkArgs(pass *analysis.Pass, args []ast.Expr, funcName, argName string) { + for _, arg := range args { + callExpr, ok := arg.(*ast.CallExpr) + if !ok { + continue + } + fun, ok := callExpr.Fun.(*ast.SelectorExpr) + if !ok { + continue + } + x, ok := fun.X.(*ast.Ident) + if !ok { + continue + } + targetName := x.Name + "." + fun.Sel.Name + if targetName == "os.Setenv" { + if argName == "" { + argName = "testing" + } + pass.Reportf(arg.Pos(), "os.Setenv() can be replaced by `%s.Setenv()` in %s", argName, funcName) + } + } +} + func checkIfStmt(pass *analysis.Pass, stmt *ast.IfStmt, funcName, argName string) bool { assignStmt, ok := stmt.Init.(*ast.AssignStmt) if !ok { @@ -158,6 +185,10 @@ func checkAssignStmt(pass *analysis.Pass, stmt *ast.AssignStmt, funcName, argNam return true } +func checkForStmt(pass *analysis.Pass, stmt *ast.ForStmt, funcName, argName string) { + checkStmts(pass, stmt.Body.List, funcName, argName) +} + func targetRunner(params []*ast.Field, fileName string) (string, bool) { for _, p := range params { switch typ := p.Type.(type) { @@ -196,7 +227,7 @@ func checkStarExprTarget(typ *ast.StarExpr) bool { } targetName := x.Name + "." + selector.Sel.Name switch targetName { - case "testing.T", "testing.B", "testing.F": + case "testing.T", "testing.B": return true default: return false diff --git a/tools/vendor/github.com/spf13/cobra/.golangci.yml b/tools/vendor/github.com/spf13/cobra/.golangci.yml index 2578d94b5e..2c8f4808c1 100644 --- a/tools/vendor/github.com/spf13/cobra/.golangci.yml +++ b/tools/vendor/github.com/spf13/cobra/.golangci.yml @@ -19,44 +19,39 @@ linters: disable-all: true enable: #- bodyclose - - deadcode + # - deadcode ! deprecated since v1.49.0; replaced by 'unused' #- depguard #- dogsled #- dupl - errcheck #- exhaustive #- funlen - - gas #- gochecknoinits - goconst - #- gocritic + - gocritic #- gocyclo - #- gofmt + - gofmt - goimports - - golint #- gomnd #- goprintffuncname - #- gosec - #- gosimple + - gosec + - gosimple - govet - ineffassign - - interfacer #- lll - - maligned - - megacheck - #- misspell + - misspell #- nakedret #- noctx - #- nolintlint + - nolintlint #- rowserrcheck #- scopelint - #- staticcheck - - structcheck - #- stylecheck + - staticcheck + #- structcheck ! deprecated since v1.49.0; replaced by 'unused' + - stylecheck #- typecheck - unconvert #- unparam - #- unused - - varcheck + - unused + # - varcheck ! deprecated since v1.49.0; replaced by 'unused' #- whitespace fast: false diff --git a/tools/vendor/github.com/spf13/cobra/README.md b/tools/vendor/github.com/spf13/cobra/README.md index 592c0b8ab0..6444f4b7f6 100644 --- a/tools/vendor/github.com/spf13/cobra/README.md +++ b/tools/vendor/github.com/spf13/cobra/README.md @@ -4,7 +4,7 @@ Cobra is a library for creating powerful modern CLI applications. Cobra is used in many Go projects such as [Kubernetes](https://kubernetes.io/), [Hugo](https://gohugo.io), and [GitHub CLI](https://github.com/cli/cli) to -name a few. [This list](./projects_using_cobra.md) contains a more extensive list of projects using Cobra. +name a few. [This list](site/content/projects_using_cobra.md) contains a more extensive list of projects using Cobra. [![](https://img.shields.io/github/actions/workflow/status/spf13/cobra/test.yml?branch=main&longCache=true&label=Test&logo=github%20actions&logoColor=fff)](https://github.com/spf13/cobra/actions?query=workflow%3ATest) [![Go Reference](https://pkg.go.dev/badge/github.com/spf13/cobra.svg)](https://pkg.go.dev/github.com/spf13/cobra) @@ -80,7 +80,7 @@ which maintains the same interface while adding POSIX compliance. # Installing Using Cobra is easy. First, use `go get` to install the latest version -of the library. +of the library. ``` go get -u github.com/spf13/cobra@latest @@ -105,8 +105,8 @@ go install github.com/spf13/cobra-cli@latest For complete details on using the Cobra-CLI generator, please read [The Cobra Generator README](https://github.com/spf13/cobra-cli/blob/main/README.md) -For complete details on using the Cobra library, please read the [The Cobra User Guide](user_guide.md). +For complete details on using the Cobra library, please read the [The Cobra User Guide](site/content/user_guide.md). # License -Cobra is released under the Apache 2.0 license. See [LICENSE.txt](https://github.com/spf13/cobra/blob/master/LICENSE.txt) +Cobra is released under the Apache 2.0 license. See [LICENSE.txt](LICENSE.txt) diff --git a/tools/vendor/github.com/spf13/cobra/active_help.go b/tools/vendor/github.com/spf13/cobra/active_help.go index 2d0239437a..25c30e3ccc 100644 --- a/tools/vendor/github.com/spf13/cobra/active_help.go +++ b/tools/vendor/github.com/spf13/cobra/active_help.go @@ -17,15 +17,14 @@ package cobra import ( "fmt" "os" - "strings" ) const ( activeHelpMarker = "_activeHelp_ " // The below values should not be changed: programs will be using them explicitly // in their user documentation, and users will be using them explicitly. - activeHelpEnvVarSuffix = "_ACTIVE_HELP" - activeHelpGlobalEnvVar = "COBRA_ACTIVE_HELP" + activeHelpEnvVarSuffix = "ACTIVE_HELP" + activeHelpGlobalEnvVar = configEnvVarGlobalPrefix + "_" + activeHelpEnvVarSuffix activeHelpGlobalDisable = "0" ) @@ -42,7 +41,7 @@ func AppendActiveHelp(compArray []string, activeHelpStr string) []string { // GetActiveHelpConfig returns the value of the ActiveHelp environment variable // _ACTIVE_HELP where is the name of the root command in upper -// case, with all - replaced by _. +// case, with all non-ASCII-alphanumeric characters replaced by `_`. // It will always return "0" if the global environment variable COBRA_ACTIVE_HELP // is set to "0". func GetActiveHelpConfig(cmd *Command) string { @@ -55,9 +54,7 @@ func GetActiveHelpConfig(cmd *Command) string { // activeHelpEnvVar returns the name of the program-specific ActiveHelp environment // variable. It has the format _ACTIVE_HELP where is the name of the -// root command in upper case, with all - replaced by _. +// root command in upper case, with all non-ASCII-alphanumeric characters replaced by `_`. func activeHelpEnvVar(name string) string { - // This format should not be changed: users will be using it explicitly. - activeHelpEnvVar := strings.ToUpper(fmt.Sprintf("%s%s", name, activeHelpEnvVarSuffix)) - return strings.ReplaceAll(activeHelpEnvVar, "-", "_") + return configEnvVar(name, activeHelpEnvVarSuffix) } diff --git a/tools/vendor/github.com/spf13/cobra/active_help.md b/tools/vendor/github.com/spf13/cobra/active_help.md deleted file mode 100644 index 5e7f59af38..0000000000 --- a/tools/vendor/github.com/spf13/cobra/active_help.md +++ /dev/null @@ -1,157 +0,0 @@ -# Active Help - -Active Help is a framework provided by Cobra which allows a program to define messages (hints, warnings, etc) that will be printed during program usage. It aims to make it easier for your users to learn how to use your program. If configured by the program, Active Help is printed when the user triggers shell completion. - -For example, -``` -bash-5.1$ helm repo add [tab] -You must choose a name for the repo you are adding. - -bash-5.1$ bin/helm package [tab] -Please specify the path to the chart to package - -bash-5.1$ bin/helm package [tab][tab] -bin/ internal/ scripts/ pkg/ testdata/ -``` - -**Hint**: A good place to use Active Help messages is when the normal completion system does not provide any suggestions. In such cases, Active Help nicely supplements the normal shell completions to guide the user in knowing what is expected by the program. -## Supported shells - -Active Help is currently only supported for the following shells: -- Bash (using [bash completion V2](shell_completions.md#bash-completion-v2) only). Note that bash 4.4 or higher is required for the prompt to appear when an Active Help message is printed. -- Zsh - -## Adding Active Help messages - -As Active Help uses the shell completion system, the implementation of Active Help messages is done by enhancing custom dynamic completions. If you are not familiar with dynamic completions, please refer to [Shell Completions](shell_completions.md). - -Adding Active Help is done through the use of the `cobra.AppendActiveHelp(...)` function, where the program repeatedly adds Active Help messages to the list of completions. Keep reading for details. - -### Active Help for nouns - -Adding Active Help when completing a noun is done within the `ValidArgsFunction(...)` of a command. Please notice the use of `cobra.AppendActiveHelp(...)` in the following example: - -```go -cmd := &cobra.Command{ - Use: "add [NAME] [URL]", - Short: "add a chart repository", - Args: require.ExactArgs(2), - RunE: func(cmd *cobra.Command, args []string) error { - return addRepo(args) - }, - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - var comps []string - if len(args) == 0 { - comps = cobra.AppendActiveHelp(comps, "You must choose a name for the repo you are adding") - } else if len(args) == 1 { - comps = cobra.AppendActiveHelp(comps, "You must specify the URL for the repo you are adding") - } else { - comps = cobra.AppendActiveHelp(comps, "This command does not take any more arguments") - } - return comps, cobra.ShellCompDirectiveNoFileComp - }, -} -``` -The example above defines the completions (none, in this specific example) as well as the Active Help messages for the `helm repo add` command. It yields the following behavior: -``` -bash-5.1$ helm repo add [tab] -You must choose a name for the repo you are adding - -bash-5.1$ helm repo add grafana [tab] -You must specify the URL for the repo you are adding - -bash-5.1$ helm repo add grafana https://grafana.github.io/helm-charts [tab] -This command does not take any more arguments -``` -**Hint**: As can be seen in the above example, a good place to use Active Help messages is when the normal completion system does not provide any suggestions. In such cases, Active Help nicely supplements the normal shell completions. - -### Active Help for flags - -Providing Active Help for flags is done in the same fashion as for nouns, but using the completion function registered for the flag. For example: -```go -_ = cmd.RegisterFlagCompletionFunc("version", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - if len(args) != 2 { - return cobra.AppendActiveHelp(nil, "You must first specify the chart to install before the --version flag can be completed"), cobra.ShellCompDirectiveNoFileComp - } - return compVersionFlag(args[1], toComplete) - }) -``` -The example above prints an Active Help message when not enough information was given by the user to complete the `--version` flag. -``` -bash-5.1$ bin/helm install myrelease --version 2.0.[tab] -You must first specify the chart to install before the --version flag can be completed - -bash-5.1$ bin/helm install myrelease bitnami/solr --version 2.0.[tab][tab] -2.0.1 2.0.2 2.0.3 -``` - -## User control of Active Help - -You may want to allow your users to disable Active Help or choose between different levels of Active Help. It is entirely up to the program to define the type of configurability of Active Help that it wants to offer, if any. -Allowing to configure Active Help is entirely optional; you can use Active Help in your program without doing anything about Active Help configuration. - -The way to configure Active Help is to use the program's Active Help environment -variable. That variable is named `_ACTIVE_HELP` where `` is the name of your -program in uppercase with any `-` replaced by an `_`. The variable should be set by the user to whatever -Active Help configuration values are supported by the program. - -For example, say `helm` has chosen to support three levels for Active Help: `on`, `off`, `local`. Then a user -would set the desired behavior to `local` by doing `export HELM_ACTIVE_HELP=local` in their shell. - -For simplicity, when in `cmd.ValidArgsFunction(...)` or a flag's completion function, the program should read the -Active Help configuration using the `cobra.GetActiveHelpConfig(cmd)` function and select what Active Help messages -should or should not be added (instead of reading the environment variable directly). - -For example: -```go -ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - activeHelpLevel := cobra.GetActiveHelpConfig(cmd) - - var comps []string - if len(args) == 0 { - if activeHelpLevel != "off" { - comps = cobra.AppendActiveHelp(comps, "You must choose a name for the repo you are adding") - } - } else if len(args) == 1 { - if activeHelpLevel != "off" { - comps = cobra.AppendActiveHelp(comps, "You must specify the URL for the repo you are adding") - } - } else { - if activeHelpLevel == "local" { - comps = cobra.AppendActiveHelp(comps, "This command does not take any more arguments") - } - } - return comps, cobra.ShellCompDirectiveNoFileComp -}, -``` -**Note 1**: If the `_ACTIVE_HELP` environment variable is set to the string "0", Cobra will automatically disable all Active Help output (even if some output was specified by the program using the `cobra.AppendActiveHelp(...)` function). Using "0" can simplify your code in situations where you want to blindly disable Active Help without having to call `cobra.GetActiveHelpConfig(cmd)` explicitly. - -**Note 2**: If a user wants to disable Active Help for every single program based on Cobra, she can set the environment variable `COBRA_ACTIVE_HELP` to "0". In this case `cobra.GetActiveHelpConfig(cmd)` will return "0" no matter what the variable `_ACTIVE_HELP` is set to. - -**Note 3**: If the user does not set `_ACTIVE_HELP` or `COBRA_ACTIVE_HELP` (which will be a common case), the default value for the Active Help configuration returned by `cobra.GetActiveHelpConfig(cmd)` will be the empty string. -## Active Help with Cobra's default completion command - -Cobra provides a default `completion` command for programs that wish to use it. -When using the default `completion` command, Active Help is configurable in the same -fashion as described above using environment variables. You may wish to document this in more -details for your users. - -## Debugging Active Help - -Debugging your Active Help code is done in the same way as debugging your dynamic completion code, which is with Cobra's hidden `__complete` command. Please refer to [debugging shell completion](shell_completions.md#debugging) for details. - -When debugging with the `__complete` command, if you want to specify different Active Help configurations, you should use the active help environment variable. That variable is named `_ACTIVE_HELP` where any `-` is replaced by an `_`. For example, we can test deactivating some Active Help as shown below: -``` -$ HELM_ACTIVE_HELP=1 bin/helm __complete install wordpress bitnami/h -bitnami/haproxy -bitnami/harbor -_activeHelp_ WARNING: cannot re-use a name that is still in use -:0 -Completion ended with directive: ShellCompDirectiveDefault - -$ HELM_ACTIVE_HELP=0 bin/helm __complete install wordpress bitnami/h -bitnami/haproxy -bitnami/harbor -:0 -Completion ended with directive: ShellCompDirectiveDefault -``` diff --git a/tools/vendor/github.com/spf13/cobra/args.go b/tools/vendor/github.com/spf13/cobra/args.go index e79ec33a81..ed1e70ceaa 100644 --- a/tools/vendor/github.com/spf13/cobra/args.go +++ b/tools/vendor/github.com/spf13/cobra/args.go @@ -52,9 +52,9 @@ func OnlyValidArgs(cmd *Command, args []string) error { if len(cmd.ValidArgs) > 0 { // Remove any description that may be included in ValidArgs. // A description is following a tab character. - var validArgs []string + validArgs := make([]string, 0, len(cmd.ValidArgs)) for _, v := range cmd.ValidArgs { - validArgs = append(validArgs, strings.Split(v, "\t")[0]) + validArgs = append(validArgs, strings.SplitN(v, "\t", 2)[0]) } for _, v := range args { if !stringInSlice(v, validArgs) { diff --git a/tools/vendor/github.com/spf13/cobra/bash_completions.go b/tools/vendor/github.com/spf13/cobra/bash_completions.go index 10c78847de..f4d198cbcb 100644 --- a/tools/vendor/github.com/spf13/cobra/bash_completions.go +++ b/tools/vendor/github.com/spf13/cobra/bash_completions.go @@ -85,7 +85,7 @@ __%[1]s_handle_go_custom_completion() local out requestComp lastParam lastChar comp directive args # Prepare the command to request completions for the program. - # Calling ${words[0]} instead of directly %[1]s allows to handle aliases + # Calling ${words[0]} instead of directly %[1]s allows handling aliases args=("${words[@]:1}") # Disable ActiveHelp which is not supported for bash completion v1 requestComp="%[8]s=0 ${words[0]} %[2]s ${args[*]}" @@ -597,19 +597,16 @@ func writeRequiredFlag(buf io.StringWriter, cmd *Command) { if nonCompletableFlag(flag) { return } - for key := range flag.Annotations { - switch key { - case BashCompOneRequiredFlag: - format := " must_have_one_flag+=(\"--%s" - if flag.Value.Type() != "bool" { - format += "=" - } - format += cbn - WriteStringAndCheck(buf, fmt.Sprintf(format, flag.Name)) - - if len(flag.Shorthand) > 0 { - WriteStringAndCheck(buf, fmt.Sprintf(" must_have_one_flag+=(\"-%s"+cbn, flag.Shorthand)) - } + if _, ok := flag.Annotations[BashCompOneRequiredFlag]; ok { + format := " must_have_one_flag+=(\"--%s" + if flag.Value.Type() != "bool" { + format += "=" + } + format += cbn + WriteStringAndCheck(buf, fmt.Sprintf(format, flag.Name)) + + if len(flag.Shorthand) > 0 { + WriteStringAndCheck(buf, fmt.Sprintf(" must_have_one_flag+=(\"-%s"+cbn, flag.Shorthand)) } } }) @@ -621,7 +618,7 @@ func writeRequiredNouns(buf io.StringWriter, cmd *Command) { for _, value := range cmd.ValidArgs { // Remove any description that may be included following a tab character. // Descriptions are not supported by bash completion. - value = strings.Split(value, "\t")[0] + value = strings.SplitN(value, "\t", 2)[0] WriteStringAndCheck(buf, fmt.Sprintf(" must_have_one_noun+=(%q)\n", value)) } if cmd.ValidArgsFunction != nil { diff --git a/tools/vendor/github.com/spf13/cobra/bash_completions.md b/tools/vendor/github.com/spf13/cobra/bash_completions.md deleted file mode 100644 index 52919b2fa6..0000000000 --- a/tools/vendor/github.com/spf13/cobra/bash_completions.md +++ /dev/null @@ -1,93 +0,0 @@ -# Generating Bash Completions For Your cobra.Command - -Please refer to [Shell Completions](shell_completions.md) for details. - -## Bash legacy dynamic completions - -For backward compatibility, Cobra still supports its legacy dynamic completion solution (described below). Unlike the `ValidArgsFunction` solution, the legacy solution will only work for Bash shell-completion and not for other shells. This legacy solution can be used along-side `ValidArgsFunction` and `RegisterFlagCompletionFunc()`, as long as both solutions are not used for the same command. This provides a path to gradually migrate from the legacy solution to the new solution. - -**Note**: Cobra's default `completion` command uses bash completion V2. If you are currently using Cobra's legacy dynamic completion solution, you should not use the default `completion` command but continue using your own. - -The legacy solution allows you to inject bash functions into the bash completion script. Those bash functions are responsible for providing the completion choices for your own completions. - -Some code that works in kubernetes: - -```bash -const ( - bash_completion_func = `__kubectl_parse_get() -{ - local kubectl_output out - if kubectl_output=$(kubectl get --no-headers "$1" 2>/dev/null); then - out=($(echo "${kubectl_output}" | awk '{print $1}')) - COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) ) - fi -} - -__kubectl_get_resource() -{ - if [[ ${#nouns[@]} -eq 0 ]]; then - return 1 - fi - __kubectl_parse_get ${nouns[${#nouns[@]} -1]} - if [[ $? -eq 0 ]]; then - return 0 - fi -} - -__kubectl_custom_func() { - case ${last_command} in - kubectl_get | kubectl_describe | kubectl_delete | kubectl_stop) - __kubectl_get_resource - return - ;; - *) - ;; - esac -} -`) -``` - -And then I set that in my command definition: - -```go -cmds := &cobra.Command{ - Use: "kubectl", - Short: "kubectl controls the Kubernetes cluster manager", - Long: `kubectl controls the Kubernetes cluster manager. - -Find more information at https://github.com/GoogleCloudPlatform/kubernetes.`, - Run: runHelp, - BashCompletionFunction: bash_completion_func, -} -``` - -The `BashCompletionFunction` option is really only valid/useful on the root command. Doing the above will cause `__kubectl_custom_func()` (`___custom_func()`) to be called when the built in processor was unable to find a solution. In the case of kubernetes a valid command might look something like `kubectl get pod [mypod]`. If you type `kubectl get pod [tab][tab]` the `__kubectl_customc_func()` will run because the cobra.Command only understood "kubectl" and "get." `__kubectl_custom_func()` will see that the cobra.Command is "kubectl_get" and will thus call another helper `__kubectl_get_resource()`. `__kubectl_get_resource` will look at the 'nouns' collected. In our example the only noun will be `pod`. So it will call `__kubectl_parse_get pod`. `__kubectl_parse_get` will actually call out to kubernetes and get any pods. It will then set `COMPREPLY` to valid pods! - -Similarly, for flags: - -```go - annotation := make(map[string][]string) - annotation[cobra.BashCompCustom] = []string{"__kubectl_get_namespaces"} - - flag := &pflag.Flag{ - Name: "namespace", - Usage: usage, - Annotations: annotation, - } - cmd.Flags().AddFlag(flag) -``` - -In addition add the `__kubectl_get_namespaces` implementation in the `BashCompletionFunction` -value, e.g.: - -```bash -__kubectl_get_namespaces() -{ - local template - template="{{ range .items }}{{ .metadata.name }} {{ end }}" - local kubectl_out - if kubectl_out=$(kubectl get -o template --template="${template}" namespace 2>/dev/null); then - COMPREPLY=( $( compgen -W "${kubectl_out}[*]" -- "$cur" ) ) - fi -} -``` diff --git a/tools/vendor/github.com/spf13/cobra/bash_completionsV2.go b/tools/vendor/github.com/spf13/cobra/bash_completionsV2.go index 19b09560c1..1cce5c329c 100644 --- a/tools/vendor/github.com/spf13/cobra/bash_completionsV2.go +++ b/tools/vendor/github.com/spf13/cobra/bash_completionsV2.go @@ -57,7 +57,7 @@ __%[1]s_get_completion_results() { local requestComp lastParam lastChar args # Prepare the command to request completions for the program. - # Calling ${words[0]} instead of directly %[1]s allows to handle aliases + # Calling ${words[0]} instead of directly %[1]s allows handling aliases args=("${words[@]:1}") requestComp="${words[0]} %[2]s ${args[*]}" diff --git a/tools/vendor/github.com/spf13/cobra/cobra.go b/tools/vendor/github.com/spf13/cobra/cobra.go index b07b44a0ce..e0b0947b04 100644 --- a/tools/vendor/github.com/spf13/cobra/cobra.go +++ b/tools/vendor/github.com/spf13/cobra/cobra.go @@ -43,12 +43,13 @@ var initializers []func() var finalizers []func() const ( - defaultPrefixMatching = false - defaultCommandSorting = true - defaultCaseInsensitive = false + defaultPrefixMatching = false + defaultCommandSorting = true + defaultCaseInsensitive = false + defaultTraverseRunHooks = false ) -// EnablePrefixMatching allows to set automatic prefix matching. Automatic prefix matching can be a dangerous thing +// EnablePrefixMatching allows setting automatic prefix matching. Automatic prefix matching can be a dangerous thing // to automatically enable in CLI tools. // Set this to true to enable it. var EnablePrefixMatching = defaultPrefixMatching @@ -60,6 +61,10 @@ var EnableCommandSorting = defaultCommandSorting // EnableCaseInsensitive allows case-insensitive commands names. (case sensitive by default) var EnableCaseInsensitive = defaultCaseInsensitive +// EnableTraverseRunHooks executes persistent pre-run and post-run hooks from all parents. +// By default this is disabled, which means only the first run hook to be found is executed. +var EnableTraverseRunHooks = defaultTraverseRunHooks + // MousetrapHelpText enables an information splash screen on Windows // if the CLI is started from explorer.exe. // To disable the mousetrap, just set this variable to blank string (""). @@ -188,8 +193,6 @@ func ld(s, t string, ignoreCase bool) int { d := make([][]int, len(s)+1) for i := range d { d[i] = make([]int, len(t)+1) - } - for i := range d { d[i][0] = i } for j := range d[0] { diff --git a/tools/vendor/github.com/spf13/cobra/command.go b/tools/vendor/github.com/spf13/cobra/command.go index 01f7c6f1c5..54748fc67e 100644 --- a/tools/vendor/github.com/spf13/cobra/command.go +++ b/tools/vendor/github.com/spf13/cobra/command.go @@ -30,7 +30,10 @@ import ( flag "github.com/spf13/pflag" ) -const FlagSetByCobraAnnotation = "cobra_annotation_flag_set_by_cobra" +const ( + FlagSetByCobraAnnotation = "cobra_annotation_flag_set_by_cobra" + CommandDisplayNameAnnotation = "cobra_annotation_command_display_name" +) // FParseErrWhitelist configures Flag parse errors to be ignored type FParseErrWhitelist flag.ParseErrorsWhitelist @@ -99,7 +102,7 @@ type Command struct { Deprecated string // Annotations are key/value pairs that can be used by applications to identify or - // group commands. + // group commands or set special options. Annotations map[string]string // Version defines the version for this command. If this value is non-empty and the command does not @@ -115,6 +118,8 @@ type Command struct { // * PostRun() // * PersistentPostRun() // All functions get the same args, the arguments after the command name. + // The *PreRun and *PostRun functions will only be executed if the Run function of the current + // command has been declared. // // PersistentPreRun: children of this command will inherit and execute. PersistentPreRun func(cmd *Command, args []string) @@ -149,8 +154,10 @@ type Command struct { // pflags contains persistent flags. pflags *flag.FlagSet // lflags contains local flags. + // This field does not represent internal state, it's used as a cache to optimise LocalFlags function call lflags *flag.FlagSet // iflags contains inherited flags. + // This field does not represent internal state, it's used as a cache to optimise InheritedFlags function call iflags *flag.FlagSet // parentsPflags is all persistent flags of cmd's parents. parentsPflags *flag.FlagSet @@ -181,6 +188,9 @@ type Command struct { // versionTemplate is the version template defined by user. versionTemplate string + // errPrefix is the error message prefix defined by user. + errPrefix string + // inReader is a reader defined by the user that replaces stdin inReader io.Reader // outWriter is a writer defined by the user that replaces stdout @@ -346,6 +356,11 @@ func (c *Command) SetVersionTemplate(s string) { c.versionTemplate = s } +// SetErrPrefix sets error message prefix to be used. Application can use it to set custom prefix. +func (c *Command) SetErrPrefix(s string) { + c.errPrefix = s +} + // SetGlobalNormalizationFunc sets a normalization function to all flag sets and also to child commands. // The user should not have a cyclic dependency on commands. func (c *Command) SetGlobalNormalizationFunc(n func(f *flag.FlagSet, name string) flag.NormalizedName) { @@ -595,6 +610,18 @@ func (c *Command) VersionTemplate() string { ` } +// ErrPrefix return error message prefix for the command +func (c *Command) ErrPrefix() string { + if c.errPrefix != "" { + return c.errPrefix + } + + if c.HasParent() { + return c.parent.ErrPrefix() + } + return "Error:" +} + func hasNoOptDefVal(name string, fs *flag.FlagSet) bool { flag := fs.Lookup(name) if flag == nil { @@ -681,7 +708,7 @@ Loop: // This is not a flag or a flag value. Check to see if it matches what we're looking for, and if so, // return the args, excluding the one at this position. if s == x { - ret := []string{} + ret := make([]string, 0, len(args)-1) ret = append(ret, args[:pos]...) ret = append(ret, args[pos+1:]...) return ret @@ -729,14 +756,14 @@ func (c *Command) findSuggestions(arg string) string { if c.SuggestionsMinimumDistance <= 0 { c.SuggestionsMinimumDistance = 2 } - suggestionsString := "" + var sb strings.Builder if suggestions := c.SuggestionsFor(arg); len(suggestions) > 0 { - suggestionsString += "\n\nDid you mean this?\n" + sb.WriteString("\n\nDid you mean this?\n") for _, s := range suggestions { - suggestionsString += fmt.Sprintf("\t%v\n", s) + _, _ = fmt.Fprintf(&sb, "\t%v\n", s) } } - return suggestionsString + return sb.String() } func (c *Command) findNext(next string) *Command { @@ -752,7 +779,9 @@ func (c *Command) findNext(next string) *Command { } if len(matches) == 1 { - return matches[0] + // Temporarily disable gosec G602, which produces a false positive. + // See https://github.com/securego/gosec/issues/1005. + return matches[0] // #nosec G602 } return nil @@ -846,7 +875,7 @@ func (c *Command) ArgsLenAtDash() int { func (c *Command) execute(a []string) (err error) { if c == nil { - return fmt.Errorf("Called Execute() on a nil Command") + return fmt.Errorf("called Execute() on a nil Command") } if len(c.Deprecated) > 0 { @@ -910,15 +939,31 @@ func (c *Command) execute(a []string) (err error) { return err } + parents := make([]*Command, 0, 5) for p := c; p != nil; p = p.Parent() { + if EnableTraverseRunHooks { + // When EnableTraverseRunHooks is set: + // - Execute all persistent pre-runs from the root parent till this command. + // - Execute all persistent post-runs from this command till the root parent. + parents = append([]*Command{p}, parents...) + } else { + // Otherwise, execute only the first found persistent hook. + parents = append(parents, p) + } + } + for _, p := range parents { if p.PersistentPreRunE != nil { if err := p.PersistentPreRunE(c, argWoFlags); err != nil { return err } - break + if !EnableTraverseRunHooks { + break + } } else if p.PersistentPreRun != nil { p.PersistentPreRun(c, argWoFlags) - break + if !EnableTraverseRunHooks { + break + } } } if c.PreRunE != nil { @@ -955,10 +1000,14 @@ func (c *Command) execute(a []string) (err error) { if err := p.PersistentPostRunE(c, argWoFlags); err != nil { return err } - break + if !EnableTraverseRunHooks { + break + } } else if p.PersistentPostRun != nil { p.PersistentPostRun(c, argWoFlags) - break + if !EnableTraverseRunHooks { + break + } } } @@ -1048,7 +1097,7 @@ func (c *Command) ExecuteC() (cmd *Command, err error) { c = cmd } if !c.SilenceErrors { - c.PrintErrln("Error:", err.Error()) + c.PrintErrln(c.ErrPrefix(), err.Error()) c.PrintErrf("Run '%v --help' for usage.\n", c.CommandPath()) } return c, err @@ -1077,7 +1126,7 @@ func (c *Command) ExecuteC() (cmd *Command, err error) { // If root command has SilenceErrors flagged, // all subcommands should respect it if !cmd.SilenceErrors && !c.SilenceErrors { - c.PrintErrln("Error:", err.Error()) + c.PrintErrln(cmd.ErrPrefix(), err.Error()) } // If root command has SilenceUsage flagged, @@ -1140,10 +1189,11 @@ func (c *Command) InitDefaultHelpFlag() { c.mergePersistentFlags() if c.Flags().Lookup("help") == nil { usage := "help for " - if c.Name() == "" { + name := c.displayName() + if name == "" { usage += "this command" } else { - usage += c.Name() + usage += name } c.Flags().BoolP("help", "h", false, usage) _ = c.Flags().SetAnnotation("help", FlagSetByCobraAnnotation, []string{"true"}) @@ -1189,7 +1239,7 @@ func (c *Command) InitDefaultHelpCmd() { Use: "help [command]", Short: "Help about any command", Long: `Help provides help for any command in the application. -Simply type ` + c.Name() + ` help [path to command] for full details.`, +Simply type ` + c.displayName() + ` help [path to command] for full details.`, ValidArgsFunction: func(c *Command, args []string, toComplete string) ([]string, ShellCompDirective) { var completions []string cmd, _, e := c.Root().Find(args) @@ -1380,16 +1430,24 @@ func (c *Command) CommandPath() string { if c.HasParent() { return c.Parent().CommandPath() + " " + c.Name() } + return c.displayName() +} + +func (c *Command) displayName() string { + if displayName, ok := c.Annotations[CommandDisplayNameAnnotation]; ok { + return displayName + } return c.Name() } // UseLine puts out the full usage for a given command (including parents). func (c *Command) UseLine() string { var useline string + use := strings.Replace(c.Use, c.Name(), c.displayName(), 1) if c.HasParent() { - useline = c.parent.CommandPath() + " " + c.Use + useline = c.parent.CommandPath() + " " + use } else { - useline = c.Use + useline = use } if c.DisableFlagsInUseLine { return useline @@ -1591,7 +1649,7 @@ func (c *Command) GlobalNormalizationFunc() func(f *flag.FlagSet, name string) f // to this command (local and persistent declared here and by all parents). func (c *Command) Flags() *flag.FlagSet { if c.flags == nil { - c.flags = flag.NewFlagSet(c.Name(), flag.ContinueOnError) + c.flags = flag.NewFlagSet(c.displayName(), flag.ContinueOnError) if c.flagErrorBuf == nil { c.flagErrorBuf = new(bytes.Buffer) } @@ -1602,10 +1660,11 @@ func (c *Command) Flags() *flag.FlagSet { } // LocalNonPersistentFlags are flags specific to this command which will NOT persist to subcommands. +// This function does not modify the flags of the current command, it's purpose is to return the current state. func (c *Command) LocalNonPersistentFlags() *flag.FlagSet { persistentFlags := c.PersistentFlags() - out := flag.NewFlagSet(c.Name(), flag.ContinueOnError) + out := flag.NewFlagSet(c.displayName(), flag.ContinueOnError) c.LocalFlags().VisitAll(func(f *flag.Flag) { if persistentFlags.Lookup(f.Name) == nil { out.AddFlag(f) @@ -1615,11 +1674,12 @@ func (c *Command) LocalNonPersistentFlags() *flag.FlagSet { } // LocalFlags returns the local FlagSet specifically set in the current command. +// This function does not modify the flags of the current command, it's purpose is to return the current state. func (c *Command) LocalFlags() *flag.FlagSet { c.mergePersistentFlags() if c.lflags == nil { - c.lflags = flag.NewFlagSet(c.Name(), flag.ContinueOnError) + c.lflags = flag.NewFlagSet(c.displayName(), flag.ContinueOnError) if c.flagErrorBuf == nil { c.flagErrorBuf = new(bytes.Buffer) } @@ -1642,11 +1702,12 @@ func (c *Command) LocalFlags() *flag.FlagSet { } // InheritedFlags returns all flags which were inherited from parent commands. +// This function does not modify the flags of the current command, it's purpose is to return the current state. func (c *Command) InheritedFlags() *flag.FlagSet { c.mergePersistentFlags() if c.iflags == nil { - c.iflags = flag.NewFlagSet(c.Name(), flag.ContinueOnError) + c.iflags = flag.NewFlagSet(c.displayName(), flag.ContinueOnError) if c.flagErrorBuf == nil { c.flagErrorBuf = new(bytes.Buffer) } @@ -1667,6 +1728,7 @@ func (c *Command) InheritedFlags() *flag.FlagSet { } // NonInheritedFlags returns all flags which were not inherited from parent commands. +// This function does not modify the flags of the current command, it's purpose is to return the current state. func (c *Command) NonInheritedFlags() *flag.FlagSet { return c.LocalFlags() } @@ -1674,7 +1736,7 @@ func (c *Command) NonInheritedFlags() *flag.FlagSet { // PersistentFlags returns the persistent FlagSet specifically set in the current command. func (c *Command) PersistentFlags() *flag.FlagSet { if c.pflags == nil { - c.pflags = flag.NewFlagSet(c.Name(), flag.ContinueOnError) + c.pflags = flag.NewFlagSet(c.displayName(), flag.ContinueOnError) if c.flagErrorBuf == nil { c.flagErrorBuf = new(bytes.Buffer) } @@ -1687,9 +1749,9 @@ func (c *Command) PersistentFlags() *flag.FlagSet { func (c *Command) ResetFlags() { c.flagErrorBuf = new(bytes.Buffer) c.flagErrorBuf.Reset() - c.flags = flag.NewFlagSet(c.Name(), flag.ContinueOnError) + c.flags = flag.NewFlagSet(c.displayName(), flag.ContinueOnError) c.flags.SetOutput(c.flagErrorBuf) - c.pflags = flag.NewFlagSet(c.Name(), flag.ContinueOnError) + c.pflags = flag.NewFlagSet(c.displayName(), flag.ContinueOnError) c.pflags.SetOutput(c.flagErrorBuf) c.lflags = nil @@ -1806,7 +1868,7 @@ func (c *Command) mergePersistentFlags() { // If c.parentsPflags == nil, it makes new. func (c *Command) updateParentsPflags() { if c.parentsPflags == nil { - c.parentsPflags = flag.NewFlagSet(c.Name(), flag.ContinueOnError) + c.parentsPflags = flag.NewFlagSet(c.displayName(), flag.ContinueOnError) c.parentsPflags.SetOutput(c.flagErrorBuf) c.parentsPflags.SortFlags = false } diff --git a/tools/vendor/github.com/spf13/cobra/completions.go b/tools/vendor/github.com/spf13/cobra/completions.go index ee38c4d0b8..c0c08b0572 100644 --- a/tools/vendor/github.com/spf13/cobra/completions.go +++ b/tools/vendor/github.com/spf13/cobra/completions.go @@ -17,6 +17,8 @@ package cobra import ( "fmt" "os" + "regexp" + "strconv" "strings" "sync" @@ -145,6 +147,20 @@ func (c *Command) RegisterFlagCompletionFunc(flagName string, f func(cmd *Comman return nil } +// GetFlagCompletionFunc returns the completion function for the given flag of the command, if available. +func (c *Command) GetFlagCompletionFunc(flagName string) (func(*Command, []string, string) ([]string, ShellCompDirective), bool) { + flag := c.Flag(flagName) + if flag == nil { + return nil, false + } + + flagCompletionMutex.RLock() + defer flagCompletionMutex.RUnlock() + + completionFunc, exists := flagCompletionFunctions[flag] + return completionFunc, exists +} + // Returns a string listing the different directive enabled in the specified parameter func (d ShellCompDirective) string() string { var directives []string @@ -197,24 +213,29 @@ func (c *Command) initCompleteCmd(args []string) { // 2- Even without completions, we need to print the directive } - noDescriptions := (cmd.CalledAs() == ShellCompNoDescRequestCmd) + noDescriptions := cmd.CalledAs() == ShellCompNoDescRequestCmd + if !noDescriptions { + if doDescriptions, err := strconv.ParseBool(getEnvConfig(cmd, configEnvVarSuffixDescriptions)); err == nil { + noDescriptions = !doDescriptions + } + } + noActiveHelp := GetActiveHelpConfig(finalCmd) == activeHelpGlobalDisable + out := finalCmd.OutOrStdout() for _, comp := range completions { - if GetActiveHelpConfig(finalCmd) == activeHelpGlobalDisable { - // Remove all activeHelp entries in this case - if strings.HasPrefix(comp, activeHelpMarker) { - continue - } + if noActiveHelp && strings.HasPrefix(comp, activeHelpMarker) { + // Remove all activeHelp entries if it's disabled. + continue } if noDescriptions { // Remove any description that may be included following a tab character. - comp = strings.Split(comp, "\t")[0] + comp = strings.SplitN(comp, "\t", 2)[0] } // Make sure we only write the first line to the output. // This is needed if a description contains a linebreak. // Otherwise the shell scripts will interpret the other lines as new flags // and could therefore provide a wrong completion. - comp = strings.Split(comp, "\n")[0] + comp = strings.SplitN(comp, "\n", 2)[0] // Finally trim the completion. This is especially important to get rid // of a trailing tab when there are no description following it. @@ -223,14 +244,14 @@ func (c *Command) initCompleteCmd(args []string) { // although there is no description). comp = strings.TrimSpace(comp) - // Print each possible completion to stdout for the completion script to consume. - fmt.Fprintln(finalCmd.OutOrStdout(), comp) + // Print each possible completion to the output for the completion script to consume. + fmt.Fprintln(out, comp) } // As the last printout, print the completion directive for the completion script to parse. // The directive integer must be that last character following a single colon (:). // The completion script expects : - fmt.Fprintf(finalCmd.OutOrStdout(), ":%d\n", directive) + fmt.Fprintf(out, ":%d\n", directive) // Print some helpful info to stderr for the user to understand. // Output from stderr must be ignored by the completion script. @@ -277,15 +298,19 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi } if err != nil { // Unable to find the real command. E.g., someInvalidCmd - return c, []string{}, ShellCompDirectiveDefault, fmt.Errorf("Unable to find a command for arguments: %v", trimmedArgs) + return c, []string{}, ShellCompDirectiveDefault, fmt.Errorf("unable to find a command for arguments: %v", trimmedArgs) } finalCmd.ctx = c.ctx // These flags are normally added when `execute()` is called on `finalCmd`, // however, when doing completion, we don't call `finalCmd.execute()`. - // Let's add the --help and --version flag ourselves. - finalCmd.InitDefaultHelpFlag() - finalCmd.InitDefaultVersionFlag() + // Let's add the --help and --version flag ourselves but only if the finalCmd + // has not disabled flag parsing; if flag parsing is disabled, it is up to the + // finalCmd itself to handle the completion of *all* flags. + if !finalCmd.DisableFlagParsing { + finalCmd.InitDefaultHelpFlag() + finalCmd.InitDefaultVersionFlag() + } // Check if we are doing flag value completion before parsing the flags. // This is important because if we are completing a flag value, we need to also @@ -389,6 +414,11 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi finalCmd.InheritedFlags().VisitAll(func(flag *pflag.Flag) { doCompleteFlags(flag) }) + // Try to complete non-inherited flags even if DisableFlagParsing==true. + // This allows programs to tell Cobra about flags for completion even + // if the actual parsing of flags is not done by Cobra. + // For instance, Helm uses this to provide flag name completion for + // some of its plugins. finalCmd.NonInheritedFlags().VisitAll(func(flag *pflag.Flag) { doCompleteFlags(flag) }) @@ -876,3 +906,34 @@ func CompError(msg string) { func CompErrorln(msg string) { CompError(fmt.Sprintf("%s\n", msg)) } + +// These values should not be changed: users will be using them explicitly. +const ( + configEnvVarGlobalPrefix = "COBRA" + configEnvVarSuffixDescriptions = "COMPLETION_DESCRIPTIONS" +) + +var configEnvVarPrefixSubstRegexp = regexp.MustCompile(`[^A-Z0-9_]`) + +// configEnvVar returns the name of the program-specific configuration environment +// variable. It has the format _ where is the name of the +// root command in upper case, with all non-ASCII-alphanumeric characters replaced by `_`. +func configEnvVar(name, suffix string) string { + // This format should not be changed: users will be using it explicitly. + v := strings.ToUpper(fmt.Sprintf("%s_%s", name, suffix)) + v = configEnvVarPrefixSubstRegexp.ReplaceAllString(v, "_") + return v +} + +// getEnvConfig returns the value of the configuration environment variable +// _ where is the name of the root command in upper +// case, with all non-ASCII-alphanumeric characters replaced by `_`. +// If the value is empty or not set, the value of the environment variable +// COBRA_ is returned instead. +func getEnvConfig(cmd *Command, suffix string) string { + v := os.Getenv(configEnvVar(cmd.Root().Name(), suffix)) + if v == "" { + v = os.Getenv(configEnvVar(configEnvVarGlobalPrefix, suffix)) + } + return v +} diff --git a/tools/vendor/github.com/spf13/cobra/fish_completions.go b/tools/vendor/github.com/spf13/cobra/fish_completions.go index 12ca0d2b11..12d61b6911 100644 --- a/tools/vendor/github.com/spf13/cobra/fish_completions.go +++ b/tools/vendor/github.com/spf13/cobra/fish_completions.go @@ -113,7 +113,7 @@ function __%[1]s_clear_perform_completion_once_result __%[1]s_debug "" __%[1]s_debug "========= clearing previously set __%[1]s_perform_completion_once_result variable ==========" set --erase __%[1]s_perform_completion_once_result - __%[1]s_debug "Succesfully erased the variable __%[1]s_perform_completion_once_result" + __%[1]s_debug "Successfully erased the variable __%[1]s_perform_completion_once_result" end function __%[1]s_requires_order_preservation diff --git a/tools/vendor/github.com/spf13/cobra/fish_completions.md b/tools/vendor/github.com/spf13/cobra/fish_completions.md deleted file mode 100644 index 19b2ed1293..0000000000 --- a/tools/vendor/github.com/spf13/cobra/fish_completions.md +++ /dev/null @@ -1,4 +0,0 @@ -## Generating Fish Completions For Your cobra.Command - -Please refer to [Shell Completions](shell_completions.md) for details. - diff --git a/tools/vendor/github.com/spf13/cobra/flag_groups.go b/tools/vendor/github.com/spf13/cobra/flag_groups.go index b35fde1554..560612fd33 100644 --- a/tools/vendor/github.com/spf13/cobra/flag_groups.go +++ b/tools/vendor/github.com/spf13/cobra/flag_groups.go @@ -23,8 +23,9 @@ import ( ) const ( - requiredAsGroup = "cobra_annotation_required_if_others_set" - mutuallyExclusive = "cobra_annotation_mutually_exclusive" + requiredAsGroupAnnotation = "cobra_annotation_required_if_others_set" + oneRequiredAnnotation = "cobra_annotation_one_required" + mutuallyExclusiveAnnotation = "cobra_annotation_mutually_exclusive" ) // MarkFlagsRequiredTogether marks the given flags with annotations so that Cobra errors @@ -36,7 +37,23 @@ func (c *Command) MarkFlagsRequiredTogether(flagNames ...string) { if f == nil { panic(fmt.Sprintf("Failed to find flag %q and mark it as being required in a flag group", v)) } - if err := c.Flags().SetAnnotation(v, requiredAsGroup, append(f.Annotations[requiredAsGroup], strings.Join(flagNames, " "))); err != nil { + if err := c.Flags().SetAnnotation(v, requiredAsGroupAnnotation, append(f.Annotations[requiredAsGroupAnnotation], strings.Join(flagNames, " "))); err != nil { + // Only errs if the flag isn't found. + panic(err) + } + } +} + +// MarkFlagsOneRequired marks the given flags with annotations so that Cobra errors +// if the command is invoked without at least one flag from the given set of flags. +func (c *Command) MarkFlagsOneRequired(flagNames ...string) { + c.mergePersistentFlags() + for _, v := range flagNames { + f := c.Flags().Lookup(v) + if f == nil { + panic(fmt.Sprintf("Failed to find flag %q and mark it as being in a one-required flag group", v)) + } + if err := c.Flags().SetAnnotation(v, oneRequiredAnnotation, append(f.Annotations[oneRequiredAnnotation], strings.Join(flagNames, " "))); err != nil { // Only errs if the flag isn't found. panic(err) } @@ -53,13 +70,13 @@ func (c *Command) MarkFlagsMutuallyExclusive(flagNames ...string) { panic(fmt.Sprintf("Failed to find flag %q and mark it as being in a mutually exclusive flag group", v)) } // Each time this is called is a single new entry; this allows it to be a member of multiple groups if needed. - if err := c.Flags().SetAnnotation(v, mutuallyExclusive, append(f.Annotations[mutuallyExclusive], strings.Join(flagNames, " "))); err != nil { + if err := c.Flags().SetAnnotation(v, mutuallyExclusiveAnnotation, append(f.Annotations[mutuallyExclusiveAnnotation], strings.Join(flagNames, " "))); err != nil { panic(err) } } } -// ValidateFlagGroups validates the mutuallyExclusive/requiredAsGroup logic and returns the +// ValidateFlagGroups validates the mutuallyExclusive/oneRequired/requiredAsGroup logic and returns the // first error encountered. func (c *Command) ValidateFlagGroups() error { if c.DisableFlagParsing { @@ -71,15 +88,20 @@ func (c *Command) ValidateFlagGroups() error { // groupStatus format is the list of flags as a unique ID, // then a map of each flag name and whether it is set or not. groupStatus := map[string]map[string]bool{} + oneRequiredGroupStatus := map[string]map[string]bool{} mutuallyExclusiveGroupStatus := map[string]map[string]bool{} flags.VisitAll(func(pflag *flag.Flag) { - processFlagForGroupAnnotation(flags, pflag, requiredAsGroup, groupStatus) - processFlagForGroupAnnotation(flags, pflag, mutuallyExclusive, mutuallyExclusiveGroupStatus) + processFlagForGroupAnnotation(flags, pflag, requiredAsGroupAnnotation, groupStatus) + processFlagForGroupAnnotation(flags, pflag, oneRequiredAnnotation, oneRequiredGroupStatus) + processFlagForGroupAnnotation(flags, pflag, mutuallyExclusiveAnnotation, mutuallyExclusiveGroupStatus) }) if err := validateRequiredFlagGroups(groupStatus); err != nil { return err } + if err := validateOneRequiredFlagGroups(oneRequiredGroupStatus); err != nil { + return err + } if err := validateExclusiveFlagGroups(mutuallyExclusiveGroupStatus); err != nil { return err } @@ -108,7 +130,7 @@ func processFlagForGroupAnnotation(flags *flag.FlagSet, pflag *flag.Flag, annota continue } - groupStatus[group] = map[string]bool{} + groupStatus[group] = make(map[string]bool, len(flagnames)) for _, name := range flagnames { groupStatus[group][name] = false } @@ -142,6 +164,27 @@ func validateRequiredFlagGroups(data map[string]map[string]bool) error { return nil } +func validateOneRequiredFlagGroups(data map[string]map[string]bool) error { + keys := sortedKeys(data) + for _, flagList := range keys { + flagnameAndStatus := data[flagList] + var set []string + for flagname, isSet := range flagnameAndStatus { + if isSet { + set = append(set, flagname) + } + } + if len(set) >= 1 { + continue + } + + // Sort values, so they can be tested/scripted against consistently. + sort.Strings(set) + return fmt.Errorf("at least one of the flags in the group [%v] is required", flagList) + } + return nil +} + func validateExclusiveFlagGroups(data map[string]map[string]bool) error { keys := sortedKeys(data) for _, flagList := range keys { @@ -176,6 +219,7 @@ func sortedKeys(m map[string]map[string]bool) []string { // enforceFlagGroupsForCompletion will do the following: // - when a flag in a group is present, other flags in the group will be marked required +// - when none of the flags in a one-required group are present, all flags in the group will be marked required // - when a flag in a mutually exclusive group is present, other flags in the group will be marked as hidden // This allows the standard completion logic to behave appropriately for flag groups func (c *Command) enforceFlagGroupsForCompletion() { @@ -185,10 +229,12 @@ func (c *Command) enforceFlagGroupsForCompletion() { flags := c.Flags() groupStatus := map[string]map[string]bool{} + oneRequiredGroupStatus := map[string]map[string]bool{} mutuallyExclusiveGroupStatus := map[string]map[string]bool{} c.Flags().VisitAll(func(pflag *flag.Flag) { - processFlagForGroupAnnotation(flags, pflag, requiredAsGroup, groupStatus) - processFlagForGroupAnnotation(flags, pflag, mutuallyExclusive, mutuallyExclusiveGroupStatus) + processFlagForGroupAnnotation(flags, pflag, requiredAsGroupAnnotation, groupStatus) + processFlagForGroupAnnotation(flags, pflag, oneRequiredAnnotation, oneRequiredGroupStatus) + processFlagForGroupAnnotation(flags, pflag, mutuallyExclusiveAnnotation, mutuallyExclusiveGroupStatus) }) // If a flag that is part of a group is present, we make all the other flags @@ -204,6 +250,26 @@ func (c *Command) enforceFlagGroupsForCompletion() { } } + // If none of the flags of a one-required group are present, we make all the flags + // of that group required so that the shell completion suggests them automatically + for flagList, flagnameAndStatus := range oneRequiredGroupStatus { + isSet := false + + for _, isSet = range flagnameAndStatus { + if isSet { + break + } + } + + // None of the flags of the group are set, mark all flags in the group + // as required + if !isSet { + for _, fName := range strings.Split(flagList, " ") { + _ = c.MarkFlagRequired(fName) + } + } + } + // If a flag that is mutually exclusive to others is present, we hide the other // flags of that group so the shell completion does not suggest them for flagList, flagnameAndStatus := range mutuallyExclusiveGroupStatus { diff --git a/tools/vendor/github.com/spf13/cobra/powershell_completions.go b/tools/vendor/github.com/spf13/cobra/powershell_completions.go index 177d2755f2..a830b7bcad 100644 --- a/tools/vendor/github.com/spf13/cobra/powershell_completions.go +++ b/tools/vendor/github.com/spf13/cobra/powershell_completions.go @@ -28,8 +28,8 @@ import ( func genPowerShellComp(buf io.StringWriter, name string, includeDesc bool) { // Variables should not contain a '-' or ':' character nameForVar := name - nameForVar = strings.Replace(nameForVar, "-", "_", -1) - nameForVar = strings.Replace(nameForVar, ":", "_", -1) + nameForVar = strings.ReplaceAll(nameForVar, "-", "_") + nameForVar = strings.ReplaceAll(nameForVar, ":", "_") compCmd := ShellCompRequestCmd if !includeDesc { @@ -47,7 +47,7 @@ filter __%[1]s_escapeStringWithSpecialChars { `+" $_ -replace '\\s|#|@|\\$|;|,|''|\\{|\\}|\\(|\\)|\"|`|\\||<|>|&','`$&'"+` } -[scriptblock]$__%[2]sCompleterBlock = { +[scriptblock]${__%[2]sCompleterBlock} = { param( $WordToComplete, $CommandAst, @@ -122,7 +122,7 @@ filter __%[1]s_escapeStringWithSpecialChars { __%[1]s_debug "Calling $RequestComp" # First disable ActiveHelp which is not supported for Powershell - $env:%[10]s=0 + ${env:%[10]s}=0 #call the command store the output in $out and redirect stderr and stdout to null # $Out is an array contains each line per element @@ -279,7 +279,7 @@ filter __%[1]s_escapeStringWithSpecialChars { } } -Register-ArgumentCompleter -CommandName '%[1]s' -ScriptBlock $__%[2]sCompleterBlock +Register-ArgumentCompleter -CommandName '%[1]s' -ScriptBlock ${__%[2]sCompleterBlock} `, name, nameForVar, compCmd, ShellCompDirectiveError, ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp, ShellCompDirectiveFilterFileExt, ShellCompDirectiveFilterDirs, ShellCompDirectiveKeepOrder, activeHelpEnvVar(name))) diff --git a/tools/vendor/github.com/spf13/cobra/powershell_completions.md b/tools/vendor/github.com/spf13/cobra/powershell_completions.md deleted file mode 100644 index c449f1e5c0..0000000000 --- a/tools/vendor/github.com/spf13/cobra/powershell_completions.md +++ /dev/null @@ -1,3 +0,0 @@ -# Generating PowerShell Completions For Your Own cobra.Command - -Please refer to [Shell Completions](shell_completions.md#powershell-completions) for details. diff --git a/tools/vendor/github.com/spf13/cobra/projects_using_cobra.md b/tools/vendor/github.com/spf13/cobra/projects_using_cobra.md deleted file mode 100644 index 8a291eb20e..0000000000 --- a/tools/vendor/github.com/spf13/cobra/projects_using_cobra.md +++ /dev/null @@ -1,64 +0,0 @@ -## Projects using Cobra - -- [Allero](https://github.com/allero-io/allero) -- [Arewefastyet](https://benchmark.vitess.io) -- [Arduino CLI](https://github.com/arduino/arduino-cli) -- [Bleve](https://blevesearch.com/) -- [Cilium](https://cilium.io/) -- [CloudQuery](https://github.com/cloudquery/cloudquery) -- [CockroachDB](https://www.cockroachlabs.com/) -- [Constellation](https://github.com/edgelesssys/constellation) -- [Cosmos SDK](https://github.com/cosmos/cosmos-sdk) -- [Datree](https://github.com/datreeio/datree) -- [Delve](https://github.com/derekparker/delve) -- [Docker (distribution)](https://github.com/docker/distribution) -- [Etcd](https://etcd.io/) -- [Gardener](https://github.com/gardener/gardenctl) -- [Giant Swarm's gsctl](https://github.com/giantswarm/gsctl) -- [Git Bump](https://github.com/erdaltsksn/git-bump) -- [GitHub CLI](https://github.com/cli/cli) -- [GitHub Labeler](https://github.com/erdaltsksn/gh-label) -- [Golangci-lint](https://golangci-lint.run) -- [GopherJS](https://github.com/gopherjs/gopherjs) -- [GoReleaser](https://goreleaser.com) -- [Helm](https://helm.sh) -- [Hugo](https://gohugo.io) -- [Infracost](https://github.com/infracost/infracost) -- [Istio](https://istio.io) -- [Kool](https://github.com/kool-dev/kool) -- [Kubernetes](https://kubernetes.io/) -- [Kubescape](https://github.com/kubescape/kubescape) -- [KubeVirt](https://github.com/kubevirt/kubevirt) -- [Linkerd](https://linkerd.io/) -- [Mattermost-server](https://github.com/mattermost/mattermost-server) -- [Mercure](https://mercure.rocks/) -- [Meroxa CLI](https://github.com/meroxa/cli) -- [Metal Stack CLI](https://github.com/metal-stack/metalctl) -- [Moby (former Docker)](https://github.com/moby/moby) -- [Moldy](https://github.com/Moldy-Community/moldy) -- [Multi-gitter](https://github.com/lindell/multi-gitter) -- [Nanobox](https://github.com/nanobox-io/nanobox)/[Nanopack](https://github.com/nanopack) -- [nFPM](https://nfpm.goreleaser.com) -- [Okteto](https://github.com/okteto/okteto) -- [OpenShift](https://www.openshift.com/) -- [Ory Hydra](https://github.com/ory/hydra) -- [Ory Kratos](https://github.com/ory/kratos) -- [Pixie](https://github.com/pixie-io/pixie) -- [Polygon Edge](https://github.com/0xPolygon/polygon-edge) -- [Pouch](https://github.com/alibaba/pouch) -- [ProjectAtomic (enterprise)](https://www.projectatomic.io/) -- [Prototool](https://github.com/uber/prototool) -- [Pulumi](https://www.pulumi.com) -- [QRcp](https://github.com/claudiodangelis/qrcp) -- [Random](https://github.com/erdaltsksn/random) -- [Rclone](https://rclone.org/) -- [Scaleway CLI](https://github.com/scaleway/scaleway-cli) -- [Sia](https://github.com/SiaFoundation/siad) -- [Skaffold](https://skaffold.dev/) -- [Tendermint](https://github.com/tendermint/tendermint) -- [Twitch CLI](https://github.com/twitchdev/twitch-cli) -- [UpCloud CLI (`upctl`)](https://github.com/UpCloudLtd/upcloud-cli) -- [Vitess](https://vitess.io) -- VMware's [Tanzu Community Edition](https://github.com/vmware-tanzu/community-edition) & [Tanzu Framework](https://github.com/vmware-tanzu/tanzu-framework) -- [Werf](https://werf.io/) -- [ZITADEL](https://github.com/zitadel/zitadel) diff --git a/tools/vendor/github.com/spf13/cobra/shell_completions.md b/tools/vendor/github.com/spf13/cobra/shell_completions.md deleted file mode 100644 index 065c0621d4..0000000000 --- a/tools/vendor/github.com/spf13/cobra/shell_completions.md +++ /dev/null @@ -1,576 +0,0 @@ -# Generating shell completions - -Cobra can generate shell completions for multiple shells. -The currently supported shells are: -- Bash -- Zsh -- fish -- PowerShell - -Cobra will automatically provide your program with a fully functional `completion` command, -similarly to how it provides the `help` command. - -## Creating your own completion command - -If you do not wish to use the default `completion` command, you can choose to -provide your own, which will take precedence over the default one. (This also provides -backwards-compatibility with programs that already have their own `completion` command.) - -If you are using the `cobra-cli` generator, -which can be found at [spf13/cobra-cli](https://github.com/spf13/cobra-cli), -you can create a completion command by running - -```bash -cobra-cli add completion -``` -and then modifying the generated `cmd/completion.go` file to look something like this -(writing the shell script to stdout allows the most flexible use): - -```go -var completionCmd = &cobra.Command{ - Use: "completion [bash|zsh|fish|powershell]", - Short: "Generate completion script", - Long: fmt.Sprintf(`To load completions: - -Bash: - - $ source <(%[1]s completion bash) - - # To load completions for each session, execute once: - # Linux: - $ %[1]s completion bash > /etc/bash_completion.d/%[1]s - # macOS: - $ %[1]s completion bash > $(brew --prefix)/etc/bash_completion.d/%[1]s - -Zsh: - - # If shell completion is not already enabled in your environment, - # you will need to enable it. You can execute the following once: - - $ echo "autoload -U compinit; compinit" >> ~/.zshrc - - # To load completions for each session, execute once: - $ %[1]s completion zsh > "${fpath[1]}/_%[1]s" - - # You will need to start a new shell for this setup to take effect. - -fish: - - $ %[1]s completion fish | source - - # To load completions for each session, execute once: - $ %[1]s completion fish > ~/.config/fish/completions/%[1]s.fish - -PowerShell: - - PS> %[1]s completion powershell | Out-String | Invoke-Expression - - # To load completions for every new session, run: - PS> %[1]s completion powershell > %[1]s.ps1 - # and source this file from your PowerShell profile. -`,cmd.Root().Name()), - DisableFlagsInUseLine: true, - ValidArgs: []string{"bash", "zsh", "fish", "powershell"}, - Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs), - Run: func(cmd *cobra.Command, args []string) { - switch args[0] { - case "bash": - cmd.Root().GenBashCompletion(os.Stdout) - case "zsh": - cmd.Root().GenZshCompletion(os.Stdout) - case "fish": - cmd.Root().GenFishCompletion(os.Stdout, true) - case "powershell": - cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout) - } - }, -} -``` - -**Note:** The cobra generator may include messages printed to stdout, for example, if the config file is loaded; this will break the auto-completion script so must be removed. - -## Adapting the default completion command - -Cobra provides a few options for the default `completion` command. To configure such options you must set -the `CompletionOptions` field on the *root* command. - -To tell Cobra *not* to provide the default `completion` command: -``` -rootCmd.CompletionOptions.DisableDefaultCmd = true -``` - -To tell Cobra to mark the default `completion` command as *hidden*: -``` -rootCmd.CompletionOptions.HiddenDefaultCmd = true -``` - -To tell Cobra *not* to provide the user with the `--no-descriptions` flag to the completion sub-commands: -``` -rootCmd.CompletionOptions.DisableNoDescFlag = true -``` - -To tell Cobra to completely disable descriptions for completions: -``` -rootCmd.CompletionOptions.DisableDescriptions = true -``` - -# Customizing completions - -The generated completion scripts will automatically handle completing commands and flags. However, you can make your completions much more powerful by providing information to complete your program's nouns and flag values. - -## Completion of nouns - -### Static completion of nouns - -Cobra allows you to provide a pre-defined list of completion choices for your nouns using the `ValidArgs` field. -For example, if you want `kubectl get [tab][tab]` to show a list of valid "nouns" you have to set them. -Some simplified code from `kubectl get` looks like: - -```go -validArgs = []string{ "pod", "node", "service", "replicationcontroller" } - -cmd := &cobra.Command{ - Use: "get [(-o|--output=)json|yaml|template|...] (RESOURCE [NAME] | RESOURCE/NAME ...)", - Short: "Display one or many resources", - Long: get_long, - Example: get_example, - Run: func(cmd *cobra.Command, args []string) { - cobra.CheckErr(RunGet(f, out, cmd, args)) - }, - ValidArgs: validArgs, -} -``` - -Notice we put the `ValidArgs` field on the `get` sub-command. Doing so will give results like: - -```bash -$ kubectl get [tab][tab] -node pod replicationcontroller service -``` - -#### Aliases for nouns - -If your nouns have aliases, you can define them alongside `ValidArgs` using `ArgAliases`: - -```go -argAliases = []string { "pods", "nodes", "services", "svc", "replicationcontrollers", "rc" } - -cmd := &cobra.Command{ - ... - ValidArgs: validArgs, - ArgAliases: argAliases -} -``` - -The aliases are shown to the user on tab completion only if no completions were found within sub-commands or `ValidArgs`. - -### Dynamic completion of nouns - -In some cases it is not possible to provide a list of completions in advance. Instead, the list of completions must be determined at execution-time. In a similar fashion as for static completions, you can use the `ValidArgsFunction` field to provide a Go function that Cobra will execute when it needs the list of completion choices for the nouns of a command. Note that either `ValidArgs` or `ValidArgsFunction` can be used for a single cobra command, but not both. -Simplified code from `helm status` looks like: - -```go -cmd := &cobra.Command{ - Use: "status RELEASE_NAME", - Short: "Display the status of the named release", - Long: status_long, - RunE: func(cmd *cobra.Command, args []string) { - RunGet(args[0]) - }, - ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - if len(args) != 0 { - return nil, cobra.ShellCompDirectiveNoFileComp - } - return getReleasesFromCluster(toComplete), cobra.ShellCompDirectiveNoFileComp - }, -} -``` -Where `getReleasesFromCluster()` is a Go function that obtains the list of current Helm releases running on the Kubernetes cluster. -Notice we put the `ValidArgsFunction` on the `status` sub-command. Let's assume the Helm releases on the cluster are: `harbor`, `notary`, `rook` and `thanos` then this dynamic completion will give results like: - -```bash -$ helm status [tab][tab] -harbor notary rook thanos -``` -You may have noticed the use of `cobra.ShellCompDirective`. These directives are bit fields allowing to control some shell completion behaviors for your particular completion. You can combine them with the bit-or operator such as `cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp` -```go -// Indicates that the shell will perform its default behavior after completions -// have been provided (this implies none of the other directives). -ShellCompDirectiveDefault - -// Indicates an error occurred and completions should be ignored. -ShellCompDirectiveError - -// Indicates that the shell should not add a space after the completion, -// even if there is a single completion provided. -ShellCompDirectiveNoSpace - -// Indicates that the shell should not provide file completion even when -// no completion is provided. -ShellCompDirectiveNoFileComp - -// Indicates that the returned completions should be used as file extension filters. -// For example, to complete only files of the form *.json or *.yaml: -// return []string{"yaml", "json"}, ShellCompDirectiveFilterFileExt -// For flags, using MarkFlagFilename() and MarkPersistentFlagFilename() -// is a shortcut to using this directive explicitly. -// -ShellCompDirectiveFilterFileExt - -// Indicates that only directory names should be provided in file completion. -// For example: -// return nil, ShellCompDirectiveFilterDirs -// For flags, using MarkFlagDirname() is a shortcut to using this directive explicitly. -// -// To request directory names within another directory, the returned completions -// should specify a single directory name within which to search. For example, -// to complete directories within "themes/": -// return []string{"themes"}, ShellCompDirectiveFilterDirs -// -ShellCompDirectiveFilterDirs - -// ShellCompDirectiveKeepOrder indicates that the shell should preserve the order -// in which the completions are provided -ShellCompDirectiveKeepOrder -``` - -***Note***: When using the `ValidArgsFunction`, Cobra will call your registered function after having parsed all flags and arguments provided in the command-line. You therefore don't need to do this parsing yourself. For example, when a user calls `helm status --namespace my-rook-ns [tab][tab]`, Cobra will call your registered `ValidArgsFunction` after having parsed the `--namespace` flag, as it would have done when calling the `RunE` function. - -#### Debugging - -Cobra achieves dynamic completion through the use of a hidden command called by the completion script. To debug your Go completion code, you can call this hidden command directly: -```bash -$ helm __complete status har -harbor -:4 -Completion ended with directive: ShellCompDirectiveNoFileComp # This is on stderr -``` -***Important:*** If the noun to complete is empty (when the user has not yet typed any letters of that noun), you must pass an empty parameter to the `__complete` command: -```bash -$ helm __complete status "" -harbor -notary -rook -thanos -:4 -Completion ended with directive: ShellCompDirectiveNoFileComp # This is on stderr -``` -Calling the `__complete` command directly allows you to run the Go debugger to troubleshoot your code. You can also add printouts to your code; Cobra provides the following functions to use for printouts in Go completion code: -```go -// Prints to the completion script debug file (if BASH_COMP_DEBUG_FILE -// is set to a file path) and optionally prints to stderr. -cobra.CompDebug(msg string, printToStdErr bool) { -cobra.CompDebugln(msg string, printToStdErr bool) - -// Prints to the completion script debug file (if BASH_COMP_DEBUG_FILE -// is set to a file path) and to stderr. -cobra.CompError(msg string) -cobra.CompErrorln(msg string) -``` -***Important:*** You should **not** leave traces that print directly to stdout in your completion code as they will be interpreted as completion choices by the completion script. Instead, use the cobra-provided debugging traces functions mentioned above. - -## Completions for flags - -### Mark flags as required - -Most of the time completions will only show sub-commands. But if a flag is required to make a sub-command work, you probably want it to show up when the user types [tab][tab]. You can mark a flag as 'Required' like so: - -```go -cmd.MarkFlagRequired("pod") -cmd.MarkFlagRequired("container") -``` - -and you'll get something like - -```bash -$ kubectl exec [tab][tab] --c --container= -p --pod= -``` - -### Specify dynamic flag completion - -As for nouns, Cobra provides a way of defining dynamic completion of flags. To provide a Go function that Cobra will execute when it needs the list of completion choices for a flag, you must register the function using the `command.RegisterFlagCompletionFunc()` function. - -```go -flagName := "output" -cmd.RegisterFlagCompletionFunc(flagName, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return []string{"json", "table", "yaml"}, cobra.ShellCompDirectiveDefault -}) -``` -Notice that calling `RegisterFlagCompletionFunc()` is done through the `command` with which the flag is associated. In our example this dynamic completion will give results like so: - -```bash -$ helm status --output [tab][tab] -json table yaml -``` - -#### Debugging - -You can also easily debug your Go completion code for flags: -```bash -$ helm __complete status --output "" -json -table -yaml -:4 -Completion ended with directive: ShellCompDirectiveNoFileComp # This is on stderr -``` -***Important:*** You should **not** leave traces that print to stdout in your completion code as they will be interpreted as completion choices by the completion script. Instead, use the cobra-provided debugging traces functions mentioned further above. - -### Specify valid filename extensions for flags that take a filename - -To limit completions of flag values to file names with certain extensions you can either use the different `MarkFlagFilename()` functions or a combination of `RegisterFlagCompletionFunc()` and `ShellCompDirectiveFilterFileExt`, like so: -```go -flagName := "output" -cmd.MarkFlagFilename(flagName, "yaml", "json") -``` -or -```go -flagName := "output" -cmd.RegisterFlagCompletionFunc(flagName, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return []string{"yaml", "json"}, ShellCompDirectiveFilterFileExt}) -``` - -### Limit flag completions to directory names - -To limit completions of flag values to directory names you can either use the `MarkFlagDirname()` functions or a combination of `RegisterFlagCompletionFunc()` and `ShellCompDirectiveFilterDirs`, like so: -```go -flagName := "output" -cmd.MarkFlagDirname(flagName) -``` -or -```go -flagName := "output" -cmd.RegisterFlagCompletionFunc(flagName, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return nil, cobra.ShellCompDirectiveFilterDirs -}) -``` -To limit completions of flag values to directory names *within another directory* you can use a combination of `RegisterFlagCompletionFunc()` and `ShellCompDirectiveFilterDirs` like so: -```go -flagName := "output" -cmd.RegisterFlagCompletionFunc(flagName, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return []string{"themes"}, cobra.ShellCompDirectiveFilterDirs -}) -``` -### Descriptions for completions - -Cobra provides support for completion descriptions. Such descriptions are supported for each shell -(however, for bash, it is only available in the [completion V2 version](#bash-completion-v2)). -For commands and flags, Cobra will provide the descriptions automatically, based on usage information. -For example, using zsh: -``` -$ helm s[tab] -search -- search for a keyword in charts -show -- show information of a chart -status -- displays the status of the named release -``` -while using fish: -``` -$ helm s[tab] -search (search for a keyword in charts) show (show information of a chart) status (displays the status of the named release) -``` - -Cobra allows you to add descriptions to your own completions. Simply add the description text after each completion, following a `\t` separator. This technique applies to completions returned by `ValidArgs`, `ValidArgsFunction` and `RegisterFlagCompletionFunc()`. For example: -```go -ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return []string{"harbor\tAn image registry", "thanos\tLong-term metrics"}, cobra.ShellCompDirectiveNoFileComp -}} -``` -or -```go -ValidArgs: []string{"bash\tCompletions for bash", "zsh\tCompletions for zsh"} -``` - -If you don't want to show descriptions in the completions, you can add `--no-descriptions` to the default `completion` command to disable them, like: - -```bash -$ source <(helm completion bash) -$ helm completion [tab][tab] -bash (generate autocompletion script for bash) powershell (generate autocompletion script for powershell) -fish (generate autocompletion script for fish) zsh (generate autocompletion script for zsh) - -$ source <(helm completion bash --no-descriptions) -$ helm completion [tab][tab] -bash fish powershell zsh -``` -## Bash completions - -### Dependencies - -The bash completion script generated by Cobra requires the `bash_completion` package. You should update the help text of your completion command to show how to install the `bash_completion` package ([Kubectl docs](https://kubernetes.io/docs/tasks/tools/install-kubectl/#enabling-shell-autocompletion)) - -### Aliases - -You can also configure `bash` aliases for your program and they will also support completions. - -```bash -alias aliasname=origcommand -complete -o default -F __start_origcommand aliasname - -# and now when you run `aliasname` completion will make -# suggestions as it did for `origcommand`. - -$ aliasname -completion firstcommand secondcommand -``` -### Bash legacy dynamic completions - -For backward compatibility, Cobra still supports its bash legacy dynamic completion solution. -Please refer to [Bash Completions](bash_completions.md) for details. - -### Bash completion V2 - -Cobra provides two versions for bash completion. The original bash completion (which started it all!) can be used by calling -`GenBashCompletion()` or `GenBashCompletionFile()`. - -A new V2 bash completion version is also available. This version can be used by calling `GenBashCompletionV2()` or -`GenBashCompletionFileV2()`. The V2 version does **not** support the legacy dynamic completion -(see [Bash Completions](bash_completions.md)) but instead works only with the Go dynamic completion -solution described in this document. -Unless your program already uses the legacy dynamic completion solution, it is recommended that you use the bash -completion V2 solution which provides the following extra features: -- Supports completion descriptions (like the other shells) -- Small completion script of less than 300 lines (v1 generates scripts of thousands of lines; `kubectl` for example has a bash v1 completion script of over 13K lines) -- Streamlined user experience thanks to a completion behavior aligned with the other shells - -`Bash` completion V2 supports descriptions for completions. When calling `GenBashCompletionV2()` or `GenBashCompletionFileV2()` -you must provide these functions with a parameter indicating if the completions should be annotated with a description; Cobra -will provide the description automatically based on usage information. You can choose to make this option configurable by -your users. - -``` -# With descriptions -$ helm s[tab][tab] -search (search for a keyword in charts) status (display the status of the named release) -show (show information of a chart) - -# Without descriptions -$ helm s[tab][tab] -search show status -``` -**Note**: Cobra's default `completion` command uses bash completion V2. If for some reason you need to use bash completion V1, you will need to implement your own `completion` command. -## Zsh completions - -Cobra supports native zsh completion generated from the root `cobra.Command`. -The generated completion script should be put somewhere in your `$fpath` and be named -`_`. You will need to start a new shell for the completions to become available. - -Zsh supports descriptions for completions. Cobra will provide the description automatically, -based on usage information. Cobra provides a way to completely disable such descriptions by -using `GenZshCompletionNoDesc()` or `GenZshCompletionFileNoDesc()`. You can choose to make -this a configurable option to your users. -``` -# With descriptions -$ helm s[tab] -search -- search for a keyword in charts -show -- show information of a chart -status -- displays the status of the named release - -# Without descriptions -$ helm s[tab] -search show status -``` -*Note*: Because of backward-compatibility requirements, we were forced to have a different API to disable completion descriptions between `zsh` and `fish`. - -### Limitations - -* Custom completions implemented in Bash scripting (legacy) are not supported and will be ignored for `zsh` (including the use of the `BashCompCustom` flag annotation). - * You should instead use `ValidArgsFunction` and `RegisterFlagCompletionFunc()` which are portable to the different shells (`bash`, `zsh`, `fish`, `powershell`). -* The function `MarkFlagCustom()` is not supported and will be ignored for `zsh`. - * You should instead use `RegisterFlagCompletionFunc()`. - -### Zsh completions standardization - -Cobra 1.1 standardized its zsh completion support to align it with its other shell completions. Although the API was kept backward-compatible, some small changes in behavior were introduced. -Please refer to [Zsh Completions](zsh_completions.md) for details. - -## fish completions - -Cobra supports native fish completions generated from the root `cobra.Command`. You can use the `command.GenFishCompletion()` or `command.GenFishCompletionFile()` functions. You must provide these functions with a parameter indicating if the completions should be annotated with a description; Cobra will provide the description automatically based on usage information. You can choose to make this option configurable by your users. -``` -# With descriptions -$ helm s[tab] -search (search for a keyword in charts) show (show information of a chart) status (displays the status of the named release) - -# Without descriptions -$ helm s[tab] -search show status -``` -*Note*: Because of backward-compatibility requirements, we were forced to have a different API to disable completion descriptions between `zsh` and `fish`. - -### Limitations - -* Custom completions implemented in bash scripting (legacy) are not supported and will be ignored for `fish` (including the use of the `BashCompCustom` flag annotation). - * You should instead use `ValidArgsFunction` and `RegisterFlagCompletionFunc()` which are portable to the different shells (`bash`, `zsh`, `fish`, `powershell`). -* The function `MarkFlagCustom()` is not supported and will be ignored for `fish`. - * You should instead use `RegisterFlagCompletionFunc()`. -* The following flag completion annotations are not supported and will be ignored for `fish`: - * `BashCompFilenameExt` (filtering by file extension) - * `BashCompSubdirsInDir` (filtering by directory) -* The functions corresponding to the above annotations are consequently not supported and will be ignored for `fish`: - * `MarkFlagFilename()` and `MarkPersistentFlagFilename()` (filtering by file extension) - * `MarkFlagDirname()` and `MarkPersistentFlagDirname()` (filtering by directory) -* Similarly, the following completion directives are not supported and will be ignored for `fish`: - * `ShellCompDirectiveFilterFileExt` (filtering by file extension) - * `ShellCompDirectiveFilterDirs` (filtering by directory) - -## PowerShell completions - -Cobra supports native PowerShell completions generated from the root `cobra.Command`. You can use the `command.GenPowerShellCompletion()` or `command.GenPowerShellCompletionFile()` functions. To include descriptions use `command.GenPowerShellCompletionWithDesc()` and `command.GenPowerShellCompletionFileWithDesc()`. Cobra will provide the description automatically based on usage information. You can choose to make this option configurable by your users. - -The script is designed to support all three PowerShell completion modes: - -* TabCompleteNext (default windows style - on each key press the next option is displayed) -* Complete (works like bash) -* MenuComplete (works like zsh) - -You set the mode with `Set-PSReadLineKeyHandler -Key Tab -Function `. Descriptions are only displayed when using the `Complete` or `MenuComplete` mode. - -Users need PowerShell version 5.0 or above, which comes with Windows 10 and can be downloaded separately for Windows 7 or 8.1. They can then write the completions to a file and source this file from their PowerShell profile, which is referenced by the `$Profile` environment variable. See `Get-Help about_Profiles` for more info about PowerShell profiles. - -``` -# With descriptions and Mode 'Complete' -$ helm s[tab] -search (search for a keyword in charts) show (show information of a chart) status (displays the status of the named release) - -# With descriptions and Mode 'MenuComplete' The description of the current selected value will be displayed below the suggestions. -$ helm s[tab] -search show status - -search for a keyword in charts - -# Without descriptions -$ helm s[tab] -search show status -``` -### Aliases - -You can also configure `powershell` aliases for your program and they will also support completions. - -``` -$ sal aliasname origcommand -$ Register-ArgumentCompleter -CommandName 'aliasname' -ScriptBlock $__origcommandCompleterBlock - -# and now when you run `aliasname` completion will make -# suggestions as it did for `origcommand`. - -$ aliasname -completion firstcommand secondcommand -``` -The name of the completer block variable is of the form `$__CompleterBlock` where every `-` and `:` in the program name have been replaced with `_`, to respect powershell naming syntax. - -### Limitations - -* Custom completions implemented in bash scripting (legacy) are not supported and will be ignored for `powershell` (including the use of the `BashCompCustom` flag annotation). - * You should instead use `ValidArgsFunction` and `RegisterFlagCompletionFunc()` which are portable to the different shells (`bash`, `zsh`, `fish`, `powershell`). -* The function `MarkFlagCustom()` is not supported and will be ignored for `powershell`. - * You should instead use `RegisterFlagCompletionFunc()`. -* The following flag completion annotations are not supported and will be ignored for `powershell`: - * `BashCompFilenameExt` (filtering by file extension) - * `BashCompSubdirsInDir` (filtering by directory) -* The functions corresponding to the above annotations are consequently not supported and will be ignored for `powershell`: - * `MarkFlagFilename()` and `MarkPersistentFlagFilename()` (filtering by file extension) - * `MarkFlagDirname()` and `MarkPersistentFlagDirname()` (filtering by directory) -* Similarly, the following completion directives are not supported and will be ignored for `powershell`: - * `ShellCompDirectiveFilterFileExt` (filtering by file extension) - * `ShellCompDirectiveFilterDirs` (filtering by directory) diff --git a/tools/vendor/github.com/spf13/cobra/user_guide.md b/tools/vendor/github.com/spf13/cobra/user_guide.md deleted file mode 100644 index 85201d840c..0000000000 --- a/tools/vendor/github.com/spf13/cobra/user_guide.md +++ /dev/null @@ -1,726 +0,0 @@ -# User Guide - -While you are welcome to provide your own organization, typically a Cobra-based -application will follow the following organizational structure: - -``` - ▾ appName/ - ▾ cmd/ - add.go - your.go - commands.go - here.go - main.go -``` - -In a Cobra app, typically the main.go file is very bare. It serves one purpose: initializing Cobra. - -```go -package main - -import ( - "{pathToYourApp}/cmd" -) - -func main() { - cmd.Execute() -} -``` - -## Using the Cobra Generator - -Cobra-CLI is its own program that will create your application and add any -commands you want. It's the easiest way to incorporate Cobra into your application. - -For complete details on using the Cobra generator, please refer to [The Cobra-CLI Generator README](https://github.com/spf13/cobra-cli/blob/main/README.md) - -## Using the Cobra Library - -To manually implement Cobra you need to create a bare main.go file and a rootCmd file. -You will optionally provide additional commands as you see fit. - -### Create rootCmd - -Cobra doesn't require any special constructors. Simply create your commands. - -Ideally you place this in app/cmd/root.go: - -```go -var rootCmd = &cobra.Command{ - Use: "hugo", - Short: "Hugo is a very fast static site generator", - Long: `A Fast and Flexible Static Site Generator built with - love by spf13 and friends in Go. - Complete documentation is available at https://gohugo.io/documentation/`, - Run: func(cmd *cobra.Command, args []string) { - // Do Stuff Here - }, -} - -func Execute() { - if err := rootCmd.Execute(); err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } -} -``` - -You will additionally define flags and handle configuration in your init() function. - -For example cmd/root.go: - -```go -package cmd - -import ( - "fmt" - "os" - - "github.com/spf13/cobra" - "github.com/spf13/viper" -) - -var ( - // Used for flags. - cfgFile string - userLicense string - - rootCmd = &cobra.Command{ - Use: "cobra-cli", - Short: "A generator for Cobra based Applications", - Long: `Cobra is a CLI library for Go that empowers applications. -This application is a tool to generate the needed files -to quickly create a Cobra application.`, - } -) - -// Execute executes the root command. -func Execute() error { - return rootCmd.Execute() -} - -func init() { - cobra.OnInitialize(initConfig) - - rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cobra.yaml)") - rootCmd.PersistentFlags().StringP("author", "a", "YOUR NAME", "author name for copyright attribution") - rootCmd.PersistentFlags().StringVarP(&userLicense, "license", "l", "", "name of license for the project") - rootCmd.PersistentFlags().Bool("viper", true, "use Viper for configuration") - viper.BindPFlag("author", rootCmd.PersistentFlags().Lookup("author")) - viper.BindPFlag("useViper", rootCmd.PersistentFlags().Lookup("viper")) - viper.SetDefault("author", "NAME HERE ") - viper.SetDefault("license", "apache") - - rootCmd.AddCommand(addCmd) - rootCmd.AddCommand(initCmd) -} - -func initConfig() { - if cfgFile != "" { - // Use config file from the flag. - viper.SetConfigFile(cfgFile) - } else { - // Find home directory. - home, err := os.UserHomeDir() - cobra.CheckErr(err) - - // Search config in home directory with name ".cobra" (without extension). - viper.AddConfigPath(home) - viper.SetConfigType("yaml") - viper.SetConfigName(".cobra") - } - - viper.AutomaticEnv() - - if err := viper.ReadInConfig(); err == nil { - fmt.Println("Using config file:", viper.ConfigFileUsed()) - } -} -``` - -### Create your main.go - -With the root command you need to have your main function execute it. -Execute should be run on the root for clarity, though it can be called on any command. - -In a Cobra app, typically the main.go file is very bare. It serves one purpose: to initialize Cobra. - -```go -package main - -import ( - "{pathToYourApp}/cmd" -) - -func main() { - cmd.Execute() -} -``` - -### Create additional commands - -Additional commands can be defined and typically are each given their own file -inside of the cmd/ directory. - -If you wanted to create a version command you would create cmd/version.go and -populate it with the following: - -```go -package cmd - -import ( - "fmt" - - "github.com/spf13/cobra" -) - -func init() { - rootCmd.AddCommand(versionCmd) -} - -var versionCmd = &cobra.Command{ - Use: "version", - Short: "Print the version number of Hugo", - Long: `All software has versions. This is Hugo's`, - Run: func(cmd *cobra.Command, args []string) { - fmt.Println("Hugo Static Site Generator v0.9 -- HEAD") - }, -} -``` - -### Organizing subcommands - -A command may have subcommands which in turn may have other subcommands. This is achieved by using -`AddCommand`. In some cases, especially in larger applications, each subcommand may be defined in -its own go package. - -The suggested approach is for the parent command to use `AddCommand` to add its most immediate -subcommands. For example, consider the following directory structure: - -```text -├── cmd -│   ├── root.go -│   └── sub1 -│   ├── sub1.go -│   └── sub2 -│   ├── leafA.go -│   ├── leafB.go -│   └── sub2.go -└── main.go -``` - -In this case: - -* The `init` function of `root.go` adds the command defined in `sub1.go` to the root command. -* The `init` function of `sub1.go` adds the command defined in `sub2.go` to the sub1 command. -* The `init` function of `sub2.go` adds the commands defined in `leafA.go` and `leafB.go` to the - sub2 command. - -This approach ensures the subcommands are always included at compile time while avoiding cyclic -references. - -### Returning and handling errors - -If you wish to return an error to the caller of a command, `RunE` can be used. - -```go -package cmd - -import ( - "fmt" - - "github.com/spf13/cobra" -) - -func init() { - rootCmd.AddCommand(tryCmd) -} - -var tryCmd = &cobra.Command{ - Use: "try", - Short: "Try and possibly fail at something", - RunE: func(cmd *cobra.Command, args []string) error { - if err := someFunc(); err != nil { - return err - } - return nil - }, -} -``` - -The error can then be caught at the execute function call. - -## Working with Flags - -Flags provide modifiers to control how the action command operates. - -### Assign flags to a command - -Since the flags are defined and used in different locations, we need to -define a variable outside with the correct scope to assign the flag to -work with. - -```go -var Verbose bool -var Source string -``` - -There are two different approaches to assign a flag. - -### Persistent Flags - -A flag can be 'persistent', meaning that this flag will be available to the -command it's assigned to as well as every command under that command. For -global flags, assign a flag as a persistent flag on the root. - -```go -rootCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "verbose output") -``` - -### Local Flags - -A flag can also be assigned locally, which will only apply to that specific command. - -```go -localCmd.Flags().StringVarP(&Source, "source", "s", "", "Source directory to read from") -``` - -### Local Flag on Parent Commands - -By default, Cobra only parses local flags on the target command, and any local flags on -parent commands are ignored. By enabling `Command.TraverseChildren`, Cobra will -parse local flags on each command before executing the target command. - -```go -command := cobra.Command{ - Use: "print [OPTIONS] [COMMANDS]", - TraverseChildren: true, -} -``` - -### Bind Flags with Config - -You can also bind your flags with [viper](https://github.com/spf13/viper): -```go -var author string - -func init() { - rootCmd.PersistentFlags().StringVar(&author, "author", "YOUR NAME", "Author name for copyright attribution") - viper.BindPFlag("author", rootCmd.PersistentFlags().Lookup("author")) -} -``` - -In this example, the persistent flag `author` is bound with `viper`. -**Note**: the variable `author` will not be set to the value from config, -when the `--author` flag is provided by user. - -More in [viper documentation](https://github.com/spf13/viper#working-with-flags). - -### Required flags - -Flags are optional by default. If instead you wish your command to report an error -when a flag has not been set, mark it as required: -```go -rootCmd.Flags().StringVarP(&Region, "region", "r", "", "AWS region (required)") -rootCmd.MarkFlagRequired("region") -``` - -Or, for persistent flags: -```go -rootCmd.PersistentFlags().StringVarP(&Region, "region", "r", "", "AWS region (required)") -rootCmd.MarkPersistentFlagRequired("region") -``` - -### Flag Groups - -If you have different flags that must be provided together (e.g. if they provide the `--username` flag they MUST provide the `--password` flag as well) then -Cobra can enforce that requirement: -```go -rootCmd.Flags().StringVarP(&u, "username", "u", "", "Username (required if password is set)") -rootCmd.Flags().StringVarP(&pw, "password", "p", "", "Password (required if username is set)") -rootCmd.MarkFlagsRequiredTogether("username", "password") -``` - -You can also prevent different flags from being provided together if they represent mutually -exclusive options such as specifying an output format as either `--json` or `--yaml` but never both: -```go -rootCmd.Flags().BoolVar(&ofJson, "json", false, "Output in JSON") -rootCmd.Flags().BoolVar(&ofYaml, "yaml", false, "Output in YAML") -rootCmd.MarkFlagsMutuallyExclusive("json", "yaml") -``` - -In both of these cases: - - both local and persistent flags can be used - - **NOTE:** the group is only enforced on commands where every flag is defined - - a flag may appear in multiple groups - - a group may contain any number of flags - -## Positional and Custom Arguments - -Validation of positional arguments can be specified using the `Args` field of `Command`. -The following validators are built in: - -- Number of arguments: - - `NoArgs` - report an error if there are any positional args. - - `ArbitraryArgs` - accept any number of args. - - `MinimumNArgs(int)` - report an error if less than N positional args are provided. - - `MaximumNArgs(int)` - report an error if more than N positional args are provided. - - `ExactArgs(int)` - report an error if there are not exactly N positional args. - - `RangeArgs(min, max)` - report an error if the number of args is not between `min` and `max`. -- Content of the arguments: - - `OnlyValidArgs` - report an error if there are any positional args not specified in the `ValidArgs` field of `Command`, which can optionally be set to a list of valid values for positional args. - -If `Args` is undefined or `nil`, it defaults to `ArbitraryArgs`. - -Moreover, `MatchAll(pargs ...PositionalArgs)` enables combining existing checks with arbitrary other checks. -For instance, if you want to report an error if there are not exactly N positional args OR if there are any positional -args that are not in the `ValidArgs` field of `Command`, you can call `MatchAll` on `ExactArgs` and `OnlyValidArgs`, as -shown below: - -```go -var cmd = &cobra.Command{ - Short: "hello", - Args: cobra.MatchAll(cobra.ExactArgs(2), cobra.OnlyValidArgs), - Run: func(cmd *cobra.Command, args []string) { - fmt.Println("Hello, World!") - }, -} -``` - -It is possible to set any custom validator that satisfies `func(cmd *cobra.Command, args []string) error`. -For example: - -```go -var cmd = &cobra.Command{ - Short: "hello", - Args: func(cmd *cobra.Command, args []string) error { - // Optionally run one of the validators provided by cobra - if err := cobra.MinimumNArgs(1)(cmd, args); err != nil { - return err - } - // Run the custom validation logic - if myapp.IsValidColor(args[0]) { - return nil - } - return fmt.Errorf("invalid color specified: %s", args[0]) - }, - Run: func(cmd *cobra.Command, args []string) { - fmt.Println("Hello, World!") - }, -} -``` - -## Example - -In the example below, we have defined three commands. Two are at the top level -and one (cmdTimes) is a child of one of the top commands. In this case the root -is not executable, meaning that a subcommand is required. This is accomplished -by not providing a 'Run' for the 'rootCmd'. - -We have only defined one flag for a single command. - -More documentation about flags is available at https://github.com/spf13/pflag - -```go -package main - -import ( - "fmt" - "strings" - - "github.com/spf13/cobra" -) - -func main() { - var echoTimes int - - var cmdPrint = &cobra.Command{ - Use: "print [string to print]", - Short: "Print anything to the screen", - Long: `print is for printing anything back to the screen. -For many years people have printed back to the screen.`, - Args: cobra.MinimumNArgs(1), - Run: func(cmd *cobra.Command, args []string) { - fmt.Println("Print: " + strings.Join(args, " ")) - }, - } - - var cmdEcho = &cobra.Command{ - Use: "echo [string to echo]", - Short: "Echo anything to the screen", - Long: `echo is for echoing anything back. -Echo works a lot like print, except it has a child command.`, - Args: cobra.MinimumNArgs(1), - Run: func(cmd *cobra.Command, args []string) { - fmt.Println("Echo: " + strings.Join(args, " ")) - }, - } - - var cmdTimes = &cobra.Command{ - Use: "times [string to echo]", - Short: "Echo anything to the screen more times", - Long: `echo things multiple times back to the user by providing -a count and a string.`, - Args: cobra.MinimumNArgs(1), - Run: func(cmd *cobra.Command, args []string) { - for i := 0; i < echoTimes; i++ { - fmt.Println("Echo: " + strings.Join(args, " ")) - } - }, - } - - cmdTimes.Flags().IntVarP(&echoTimes, "times", "t", 1, "times to echo the input") - - var rootCmd = &cobra.Command{Use: "app"} - rootCmd.AddCommand(cmdPrint, cmdEcho) - cmdEcho.AddCommand(cmdTimes) - rootCmd.Execute() -} -``` - -For a more complete example of a larger application, please checkout [Hugo](https://gohugo.io/). - -## Help Command - -Cobra automatically adds a help command to your application when you have subcommands. -This will be called when a user runs 'app help'. Additionally, help will also -support all other commands as input. Say, for instance, you have a command called -'create' without any additional configuration; Cobra will work when 'app help -create' is called. Every command will automatically have the '--help' flag added. - -### Example - -The following output is automatically generated by Cobra. Nothing beyond the -command and flag definitions are needed. - - $ cobra-cli help - - Cobra is a CLI library for Go that empowers applications. - This application is a tool to generate the needed files - to quickly create a Cobra application. - - Usage: - cobra-cli [command] - - Available Commands: - add Add a command to a Cobra Application - completion Generate the autocompletion script for the specified shell - help Help about any command - init Initialize a Cobra Application - - Flags: - -a, --author string author name for copyright attribution (default "YOUR NAME") - --config string config file (default is $HOME/.cobra.yaml) - -h, --help help for cobra-cli - -l, --license string name of license for the project - --viper use Viper for configuration - - Use "cobra-cli [command] --help" for more information about a command. - - -Help is just a command like any other. There is no special logic or behavior -around it. In fact, you can provide your own if you want. - -### Grouping commands in help - -Cobra supports grouping of available commands in the help output. To group commands, each group must be explicitly -defined using `AddGroup()` on the parent command. Then a subcommand can be added to a group using the `GroupID` element -of that subcommand. The groups will appear in the help output in the same order as they are defined using different -calls to `AddGroup()`. If you use the generated `help` or `completion` commands, you can set their group ids using -`SetHelpCommandGroupId()` and `SetCompletionCommandGroupId()` on the root command, respectively. - -### Defining your own help - -You can provide your own Help command or your own template for the default command to use -with the following functions: - -```go -cmd.SetHelpCommand(cmd *Command) -cmd.SetHelpFunc(f func(*Command, []string)) -cmd.SetHelpTemplate(s string) -``` - -The latter two will also apply to any children commands. - -## Usage Message - -When the user provides an invalid flag or invalid command, Cobra responds by -showing the user the 'usage'. - -### Example -You may recognize this from the help above. That's because the default help -embeds the usage as part of its output. - - $ cobra-cli --invalid - Error: unknown flag: --invalid - Usage: - cobra-cli [command] - - Available Commands: - add Add a command to a Cobra Application - completion Generate the autocompletion script for the specified shell - help Help about any command - init Initialize a Cobra Application - - Flags: - -a, --author string author name for copyright attribution (default "YOUR NAME") - --config string config file (default is $HOME/.cobra.yaml) - -h, --help help for cobra-cli - -l, --license string name of license for the project - --viper use Viper for configuration - - Use "cobra [command] --help" for more information about a command. - -### Defining your own usage -You can provide your own usage function or template for Cobra to use. -Like help, the function and template are overridable through public methods: - -```go -cmd.SetUsageFunc(f func(*Command) error) -cmd.SetUsageTemplate(s string) -``` - -## Version Flag - -Cobra adds a top-level '--version' flag if the Version field is set on the root command. -Running an application with the '--version' flag will print the version to stdout using -the version template. The template can be customized using the -`cmd.SetVersionTemplate(s string)` function. - -## PreRun and PostRun Hooks - -It is possible to run functions before or after the main `Run` function of your command. The `PersistentPreRun` and `PreRun` functions will be executed before `Run`. `PersistentPostRun` and `PostRun` will be executed after `Run`. The `Persistent*Run` functions will be inherited by children if they do not declare their own. These functions are run in the following order: - -- `PersistentPreRun` -- `PreRun` -- `Run` -- `PostRun` -- `PersistentPostRun` - -An example of two commands which use all of these features is below. When the subcommand is executed, it will run the root command's `PersistentPreRun` but not the root command's `PersistentPostRun`: - -```go -package main - -import ( - "fmt" - - "github.com/spf13/cobra" -) - -func main() { - - var rootCmd = &cobra.Command{ - Use: "root [sub]", - Short: "My root command", - PersistentPreRun: func(cmd *cobra.Command, args []string) { - fmt.Printf("Inside rootCmd PersistentPreRun with args: %v\n", args) - }, - PreRun: func(cmd *cobra.Command, args []string) { - fmt.Printf("Inside rootCmd PreRun with args: %v\n", args) - }, - Run: func(cmd *cobra.Command, args []string) { - fmt.Printf("Inside rootCmd Run with args: %v\n", args) - }, - PostRun: func(cmd *cobra.Command, args []string) { - fmt.Printf("Inside rootCmd PostRun with args: %v\n", args) - }, - PersistentPostRun: func(cmd *cobra.Command, args []string) { - fmt.Printf("Inside rootCmd PersistentPostRun with args: %v\n", args) - }, - } - - var subCmd = &cobra.Command{ - Use: "sub [no options!]", - Short: "My subcommand", - PreRun: func(cmd *cobra.Command, args []string) { - fmt.Printf("Inside subCmd PreRun with args: %v\n", args) - }, - Run: func(cmd *cobra.Command, args []string) { - fmt.Printf("Inside subCmd Run with args: %v\n", args) - }, - PostRun: func(cmd *cobra.Command, args []string) { - fmt.Printf("Inside subCmd PostRun with args: %v\n", args) - }, - PersistentPostRun: func(cmd *cobra.Command, args []string) { - fmt.Printf("Inside subCmd PersistentPostRun with args: %v\n", args) - }, - } - - rootCmd.AddCommand(subCmd) - - rootCmd.SetArgs([]string{""}) - rootCmd.Execute() - fmt.Println() - rootCmd.SetArgs([]string{"sub", "arg1", "arg2"}) - rootCmd.Execute() -} -``` - -Output: -``` -Inside rootCmd PersistentPreRun with args: [] -Inside rootCmd PreRun with args: [] -Inside rootCmd Run with args: [] -Inside rootCmd PostRun with args: [] -Inside rootCmd PersistentPostRun with args: [] - -Inside rootCmd PersistentPreRun with args: [arg1 arg2] -Inside subCmd PreRun with args: [arg1 arg2] -Inside subCmd Run with args: [arg1 arg2] -Inside subCmd PostRun with args: [arg1 arg2] -Inside subCmd PersistentPostRun with args: [arg1 arg2] -``` - -## Suggestions when "unknown command" happens - -Cobra will print automatic suggestions when "unknown command" errors happen. This allows Cobra to behave similarly to the `git` command when a typo happens. For example: - -``` -$ hugo srever -Error: unknown command "srever" for "hugo" - -Did you mean this? - server - -Run 'hugo --help' for usage. -``` - -Suggestions are automatically generated based on existing subcommands and use an implementation of [Levenshtein distance](https://en.wikipedia.org/wiki/Levenshtein_distance). Every registered command that matches a minimum distance of 2 (ignoring case) will be displayed as a suggestion. - -If you need to disable suggestions or tweak the string distance in your command, use: - -```go -command.DisableSuggestions = true -``` - -or - -```go -command.SuggestionsMinimumDistance = 1 -``` - -You can also explicitly set names for which a given command will be suggested using the `SuggestFor` attribute. This allows suggestions for strings that are not close in terms of string distance, but make sense in your set of commands but for which -you don't want aliases. Example: - -``` -$ kubectl remove -Error: unknown command "remove" for "kubectl" - -Did you mean this? - delete - -Run 'kubectl help' for usage. -``` - -## Generating documentation for your command - -Cobra can generate documentation based on subcommands, flags, etc. Read more about it in the [docs generation documentation](doc/README.md). - -## Generating shell completions - -Cobra can generate a shell-completion file for the following shells: bash, zsh, fish, PowerShell. If you add more information to your commands, these completions can be amazingly powerful and flexible. Read more about it in [Shell Completions](shell_completions.md). - -## Providing Active Help - -Cobra makes use of the shell-completion system to define a framework allowing you to provide Active Help to your users. Active Help are messages (hints, warnings, etc) printed as the program is being used. Read more about it in [Active Help](active_help.md). diff --git a/tools/vendor/github.com/spf13/cobra/zsh_completions.md b/tools/vendor/github.com/spf13/cobra/zsh_completions.md deleted file mode 100644 index 7cff61787f..0000000000 --- a/tools/vendor/github.com/spf13/cobra/zsh_completions.md +++ /dev/null @@ -1,48 +0,0 @@ -## Generating Zsh Completion For Your cobra.Command - -Please refer to [Shell Completions](shell_completions.md) for details. - -## Zsh completions standardization - -Cobra 1.1 standardized its zsh completion support to align it with its other shell completions. Although the API was kept backwards-compatible, some small changes in behavior were introduced. - -### Deprecation summary - -See further below for more details on these deprecations. - -* `cmd.MarkZshCompPositionalArgumentFile(pos, []string{})` is no longer needed. It is therefore **deprecated** and silently ignored. -* `cmd.MarkZshCompPositionalArgumentFile(pos, glob[])` is **deprecated** and silently ignored. - * Instead use `ValidArgsFunction` with `ShellCompDirectiveFilterFileExt`. -* `cmd.MarkZshCompPositionalArgumentWords()` is **deprecated** and silently ignored. - * Instead use `ValidArgsFunction`. - -### Behavioral changes - -**Noun completion** -|Old behavior|New behavior| -|---|---| -|No file completion by default (opposite of bash)|File completion by default; use `ValidArgsFunction` with `ShellCompDirectiveNoFileComp` to turn off file completion on a per-argument basis| -|Completion of flag names without the `-` prefix having been typed|Flag names are only completed if the user has typed the first `-`| -`cmd.MarkZshCompPositionalArgumentFile(pos, []string{})` used to turn on file completion on a per-argument position basis|File completion for all arguments by default; `cmd.MarkZshCompPositionalArgumentFile()` is **deprecated** and silently ignored| -|`cmd.MarkZshCompPositionalArgumentFile(pos, glob[])` used to turn on file completion **with glob filtering** on a per-argument position basis (zsh-specific)|`cmd.MarkZshCompPositionalArgumentFile()` is **deprecated** and silently ignored; use `ValidArgsFunction` with `ShellCompDirectiveFilterFileExt` for file **extension** filtering (not full glob filtering)| -|`cmd.MarkZshCompPositionalArgumentWords(pos, words[])` used to provide completion choices on a per-argument position basis (zsh-specific)|`cmd.MarkZshCompPositionalArgumentWords()` is **deprecated** and silently ignored; use `ValidArgsFunction` to achieve the same behavior| - -**Flag-value completion** - -|Old behavior|New behavior| -|---|---| -|No file completion by default (opposite of bash)|File completion by default; use `RegisterFlagCompletionFunc()` with `ShellCompDirectiveNoFileComp` to turn off file completion| -|`cmd.MarkFlagFilename(flag, []string{})` and similar used to turn on file completion|File completion by default; `cmd.MarkFlagFilename(flag, []string{})` no longer needed in this context and silently ignored| -|`cmd.MarkFlagFilename(flag, glob[])` used to turn on file completion **with glob filtering** (syntax of `[]string{"*.yaml", "*.yml"}` incompatible with bash)|Will continue to work, however, support for bash syntax is added and should be used instead so as to work for all shells (`[]string{"yaml", "yml"}`)| -|`cmd.MarkFlagDirname(flag)` only completes directories (zsh-specific)|Has been added for all shells| -|Completion of a flag name does not repeat, unless flag is of type `*Array` or `*Slice` (not supported by bash)|Retained for `zsh` and added to `fish`| -|Completion of a flag name does not provide the `=` form (unlike bash)|Retained for `zsh` and added to `fish`| - -**Improvements** - -* Custom completion support (`ValidArgsFunction` and `RegisterFlagCompletionFunc()`) -* File completion by default if no other completions found -* Handling of required flags -* File extension filtering no longer mutually exclusive with bash usage -* Completion of directory names *within* another directory -* Support for `=` form of flags diff --git a/tools/vendor/github.com/t-yuki/gocover-cobertura/.travis.yml b/tools/vendor/github.com/t-yuki/gocover-cobertura/.travis.yml deleted file mode 100644 index f17bb61693..0000000000 --- a/tools/vendor/github.com/t-yuki/gocover-cobertura/.travis.yml +++ /dev/null @@ -1,12 +0,0 @@ -language: go -go: - - 1.6 - - 1.7 - - 1.8 - - tip - -sudo: false -before_install: - - go get github.com/mattn/goveralls -script: - - $GOPATH/bin/goveralls -service=travis-ci diff --git a/tools/vendor/github.com/t-yuki/gocover-cobertura/LICENSE b/tools/vendor/github.com/t-yuki/gocover-cobertura/LICENSE deleted file mode 100644 index 7ec1b3d853..0000000000 --- a/tools/vendor/github.com/t-yuki/gocover-cobertura/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2013 Yukinari Toyota - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/tools/vendor/github.com/t-yuki/gocover-cobertura/README.md b/tools/vendor/github.com/t-yuki/gocover-cobertura/README.md deleted file mode 100644 index 60ab1dbe72..0000000000 --- a/tools/vendor/github.com/t-yuki/gocover-cobertura/README.md +++ /dev/null @@ -1,35 +0,0 @@ -[![Build Status](https://travis-ci.org/t-yuki/gocover-cobertura.svg?branch=master)](https://travis-ci.org/t-yuki/gocover-cobertura) -[![Coverage Status](https://coveralls.io/repos/github/t-yuki/gocover-cobertura/badge.svg?branch=master)](https://coveralls.io/github/t-yuki/gocover-cobertura?branch=master) - -go tool cover XML (Cobertura) export -==================================== - -This is a simple helper tool for generating XML output in [Cobertura](http://cobertura.sourceforge.net/) format -for CIs like [Jenkins](https://wiki.jenkins-ci.org/display/JENKINS/Cobertura+Plugin) and others -from [go tool cover](https://code.google.com/p/go.tools/) output. - -Installation ------------- - -Just type the following to install the program and its dependencies: - - $ go get code.google.com/p/go.tools/cmd/cover - $ go get github.com/t-yuki/gocover-cobertura - -Usage ------ - -`gocover-cobertura` reads from the standard input: - - $ go test -coverprofile=coverage.txt -covermode count github.com/gorilla/mux - $ gocover-cobertura < coverage.txt > coverage.xml - -Authors -------- - -* [Yukinari Toyota (t-yuki)](https://github.com/t-yuki) - -Thanks ------- - -This tool is originated from [gocov-xml](https://github.com/AlekSi/gocov-xml) by [Alexey Palazhchenko (AlekSi)](https://github.com/AlekSi) diff --git a/tools/vendor/github.com/t-yuki/gocover-cobertura/cobertura.go b/tools/vendor/github.com/t-yuki/gocover-cobertura/cobertura.go deleted file mode 100644 index 8556dc5636..0000000000 --- a/tools/vendor/github.com/t-yuki/gocover-cobertura/cobertura.go +++ /dev/null @@ -1,178 +0,0 @@ -package main - -import ( - "encoding/xml" -) - -type Coverage struct { - XMLName xml.Name `xml:"coverage"` - LineRate float32 `xml:"line-rate,attr"` - BranchRate float32 `xml:"branch-rate,attr"` - Version string `xml:"version,attr"` - Timestamp int64 `xml:"timestamp,attr"` - LinesCovered int64 `xml:"lines-covered,attr"` - LinesValid int64 `xml:"lines-valid,attr"` - BranchesCovered int64 `xml:"branches-covered,attr"` - BranchesValid int64 `xml:"branches-valid,attr"` - Complexity float32 `xml:"complexity,attr"` - Sources []*Source `xml:"sources>source"` - Packages []*Package `xml:"packages>package"` -} - -type Source struct { - Path string `xml:",chardata"` -} - -type Package struct { - Name string `xml:"name,attr"` - LineRate float32 `xml:"line-rate,attr"` - BranchRate float32 `xml:"branch-rate,attr"` - Complexity float32 `xml:"complexity,attr"` - Classes []*Class `xml:"classes>class"` -} - -type Class struct { - Name string `xml:"name,attr"` - Filename string `xml:"filename,attr"` - LineRate float32 `xml:"line-rate,attr"` - BranchRate float32 `xml:"branch-rate,attr"` - Complexity float32 `xml:"complexity,attr"` - Methods []*Method `xml:"methods>method"` - Lines Lines `xml:"lines>line"` -} - -type Method struct { - Name string `xml:"name,attr"` - Signature string `xml:"signature,attr"` - LineRate float32 `xml:"line-rate,attr"` - BranchRate float32 `xml:"branch-rate,attr"` - Complexity float32 `xml:"complexity,attr"` - Lines Lines `xml:"lines>line"` -} - -type Line struct { - Number int `xml:"number,attr"` - Hits int64 `xml:"hits,attr"` -} - -// Lines is a slice of Line pointers, with some convenience methods -type Lines []*Line - -// HitRate returns a float32 from 0.0 to 1.0 representing what fraction of lines -// have hits -func (lines Lines) HitRate() (hitRate float32) { - return float32(lines.NumLinesWithHits()) / float32(len(lines)) -} - -// NumLines returns the number of lines -func (lines Lines) NumLines() int64 { - return int64(len(lines)) -} - -// NumLinesWithHits returns the number of lines with a hit count > 0 -func (lines Lines) NumLinesWithHits() (numLinesWithHits int64) { - for _, line := range lines { - if line.Hits > 0 { - numLinesWithHits++ - } - } - return numLinesWithHits -} - -// AddOrUpdateLine adds a line if it is a different line than the last line recorded. -// If it's the same line as the last line recorded then we update the hits down -// if the new hits is less; otherwise just leave it as-is -func (lines *Lines) AddOrUpdateLine(lineNumber int, hits int64) { - if len(*lines) > 0 { - lastLine := (*lines)[len(*lines)-1] - if lineNumber == lastLine.Number { - if hits < lastLine.Hits { - lastLine.Hits = hits - } - return - } - } - *lines = append(*lines, &Line{Number: lineNumber, Hits: hits}) -} - -// HitRate returns a float32 from 0.0 to 1.0 representing what fraction of lines -// have hits -func (method Method) HitRate() float32 { - return method.Lines.HitRate() -} - -// NumLines returns the number of lines -func (method Method) NumLines() int64 { - return method.Lines.NumLines() -} - -// NumLinesWithHits returns the number of lines with a hit count > 0 -func (method Method) NumLinesWithHits() int64 { - return method.Lines.NumLinesWithHits() -} - -// HitRate returns a float32 from 0.0 to 1.0 representing what fraction of lines -// have hits -func (class Class) HitRate() float32 { - return float32(class.NumLinesWithHits()) / float32(class.NumLines()) -} - -// NumLines returns the number of lines -func (class Class) NumLines() (numLines int64) { - for _, method := range class.Methods { - numLines += method.NumLines() - } - return numLines -} - -// NumLinesWithHits returns the number of lines with a hit count > 0 -func (class Class) NumLinesWithHits() (numLinesWithHits int64) { - for _, method := range class.Methods { - numLinesWithHits += method.NumLinesWithHits() - } - return numLinesWithHits -} - -// HitRate returns a float32 from 0.0 to 1.0 representing what fraction of lines -// have hits -func (pkg Package) HitRate() float32 { - return float32(pkg.NumLinesWithHits()) / float32(pkg.NumLines()) -} - -// NumLines returns the number of lines -func (pkg Package) NumLines() (numLines int64) { - for _, class := range pkg.Classes { - numLines += class.NumLines() - } - return numLines -} - -// NumLinesWithHits returns the number of lines with a hit count > 0 -func (pkg Package) NumLinesWithHits() (numLinesWithHits int64) { - for _, class := range pkg.Classes { - numLinesWithHits += class.NumLinesWithHits() - } - return numLinesWithHits -} - -// HitRate returns a float32 from 0.0 to 1.0 representing what fraction of lines -// have hits -func (cov Coverage) HitRate() float32 { - return float32(cov.NumLinesWithHits()) / float32(cov.NumLines()) -} - -// NumLines returns the number of lines -func (cov Coverage) NumLines() (numLines int64) { - for _, pkg := range cov.Packages { - numLines += pkg.NumLines() - } - return numLines -} - -// NumLinesWithHits returns the number of lines with a hit count > 0 -func (cov Coverage) NumLinesWithHits() (numLinesWithHits int64) { - for _, pkg := range cov.Packages { - numLinesWithHits += pkg.NumLinesWithHits() - } - return numLinesWithHits -} diff --git a/tools/vendor/github.com/t-yuki/gocover-cobertura/gocover-cobertura.go b/tools/vendor/github.com/t-yuki/gocover-cobertura/gocover-cobertura.go deleted file mode 100644 index e64b5de029..0000000000 --- a/tools/vendor/github.com/t-yuki/gocover-cobertura/gocover-cobertura.go +++ /dev/null @@ -1,176 +0,0 @@ -package main - -import ( - "encoding/xml" - "fmt" - "go/ast" - "go/build" - "go/parser" - "go/token" - "io" - "io/ioutil" - "os" - "path/filepath" - "strings" - "time" -) - -const coberturaDTDDecl = "\n" - -func main() { - convert(os.Stdin, os.Stdout) -} - -func convert(in io.Reader, out io.Writer) { - profiles, err := ParseProfiles(in) - if err != nil { - panic("Can't parse profiles") - } - - srcDirs := build.Default.SrcDirs() - sources := make([]*Source, len(srcDirs)) - for i, dir := range srcDirs { - sources[i] = &Source{dir} - } - - coverage := Coverage{Sources: sources, Packages: nil, Timestamp: time.Now().UnixNano() / int64(time.Millisecond)} - coverage.parseProfiles(profiles) - - fmt.Fprintf(out, xml.Header) - fmt.Fprintf(out, coberturaDTDDecl) - - encoder := xml.NewEncoder(out) - encoder.Indent("", "\t") - err = encoder.Encode(coverage) - if err != nil { - panic(err) - } - - fmt.Fprintln(out) -} - -func (cov *Coverage) parseProfiles(profiles []*Profile) error { - cov.Packages = []*Package{} - for _, profile := range profiles { - cov.parseProfile(profile) - } - cov.LinesValid = cov.NumLines() - cov.LinesCovered = cov.NumLinesWithHits() - cov.LineRate = cov.HitRate() - return nil -} - -func (cov *Coverage) parseProfile(profile *Profile) error { - fileName := profile.FileName - absFilePath, err := findFile(fileName) - if err != nil { - return err - } - fset := token.NewFileSet() - parsed, err := parser.ParseFile(fset, absFilePath, nil, 0) - if err != nil { - return err - } - data, err := ioutil.ReadFile(absFilePath) - if err != nil { - return err - } - - pkgPath, _ := filepath.Split(fileName) - pkgPath = strings.TrimRight(pkgPath, string(os.PathSeparator)) - - var pkg *Package - for _, p := range cov.Packages { - if p.Name == pkgPath { - pkg = p - } - } - if pkg == nil { - pkg = &Package{Name: pkgPath, Classes: []*Class{}} - cov.Packages = append(cov.Packages, pkg) - } - visitor := &fileVisitor{ - fset: fset, - fileName: fileName, - fileData: data, - classes: make(map[string]*Class), - pkg: pkg, - profile: profile, - } - ast.Walk(visitor, parsed) - pkg.LineRate = pkg.HitRate() - return nil -} - -type fileVisitor struct { - fset *token.FileSet - fileName string - fileData []byte - pkg *Package - classes map[string]*Class - profile *Profile -} - -func (v *fileVisitor) Visit(node ast.Node) ast.Visitor { - switch n := node.(type) { - case *ast.FuncDecl: - class := v.class(n) - method := v.method(n) - method.LineRate = method.Lines.HitRate() - class.Methods = append(class.Methods, method) - for _, line := range method.Lines { - class.Lines = append(class.Lines, line) - } - class.LineRate = class.Lines.HitRate() - } - return v -} - -func (v *fileVisitor) method(n *ast.FuncDecl) *Method { - method := &Method{Name: n.Name.Name} - method.Lines = []*Line{} - - start := v.fset.Position(n.Pos()) - end := v.fset.Position(n.End()) - startLine := start.Line - startCol := start.Column - endLine := end.Line - endCol := end.Column - // The blocks are sorted, so we can stop counting as soon as we reach the end of the relevant block. - for _, b := range v.profile.Blocks { - if b.StartLine > endLine || (b.StartLine == endLine && b.StartCol >= endCol) { - // Past the end of the function. - break - } - if b.EndLine < startLine || (b.EndLine == startLine && b.EndCol <= startCol) { - // Before the beginning of the function - continue - } - for i := b.StartLine; i <= b.EndLine; i++ { - method.Lines.AddOrUpdateLine(i, int64(b.Count)) - } - } - return method -} - -func (v *fileVisitor) class(n *ast.FuncDecl) *Class { - className := v.recvName(n) - var class *Class = v.classes[className] - if class == nil { - class = &Class{Name: className, Filename: v.fileName, Methods: []*Method{}, Lines: []*Line{}} - v.classes[className] = class - v.pkg.Classes = append(v.pkg.Classes, class) - } - return class -} - -func (v *fileVisitor) recvName(n *ast.FuncDecl) string { - if n.Recv == nil { - return "-" - } - recv := n.Recv.List[0].Type - start := v.fset.Position(recv.Pos()) - end := v.fset.Position(recv.End()) - name := string(v.fileData[start.Offset:end.Offset]) - return strings.TrimSpace(strings.TrimLeft(name, "*")) -} diff --git a/tools/vendor/github.com/t-yuki/gocover-cobertura/profile.go b/tools/vendor/github.com/t-yuki/gocover-cobertura/profile.go deleted file mode 100644 index 99cbac234c..0000000000 --- a/tools/vendor/github.com/t-yuki/gocover-cobertura/profile.go +++ /dev/null @@ -1,202 +0,0 @@ -// Imported from https://code.google.com/p/go/source/browse/cmd/cover/profile.go?repo=tools&r=c10a9dd5e0b0a859a8385b6f004584cb083a3934 - -// Copyright 2013 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "bufio" - "fmt" - "go/build" - "io" - "math" - "os" - "path/filepath" - "regexp" - "sort" - "strconv" - "strings" -) - -// Profile represents the profiling data for a specific file. -type Profile struct { - FileName string - Mode string - Blocks []ProfileBlock -} - -// ProfileBlock represents a single block of profiling data. -type ProfileBlock struct { - StartLine, StartCol int - EndLine, EndCol int - NumStmt, Count int -} - -type byFileName []*Profile - -func (p byFileName) Len() int { return len(p) } -func (p byFileName) Less(i, j int) bool { return p[i].FileName < p[j].FileName } -func (p byFileName) Swap(i, j int) { p[i], p[j] = p[j], p[i] } - -// ParseProfiles parses profile data from the given Reader and returns a -// Profile for each file. -func ParseProfiles(in io.Reader) ([]*Profile, error) { - files := make(map[string]*Profile) - // First line is "mode: foo", where foo is "set", "count", or "atomic". - // Rest of file is in the format - // encoding/base64/base64.go:34.44,37.40 3 1 - // where the fields are: name.go:line.column,line.column numberOfStatements count - s := bufio.NewScanner(in) - mode := "" - for s.Scan() { - line := s.Text() - if mode == "" { - const p = "mode: " - if !strings.HasPrefix(line, p) || line == p { - return nil, fmt.Errorf("bad mode line: %v", line) - } - mode = line[len(p):] - continue - } - m := lineRe.FindStringSubmatch(line) - if m == nil { - return nil, fmt.Errorf("line %q doesn't match expected format: %v", m, lineRe) - } - fn := m[1] - p := files[fn] - if p == nil { - p = &Profile{ - FileName: fn, - Mode: mode, - } - files[fn] = p - } - p.Blocks = append(p.Blocks, ProfileBlock{ - StartLine: toInt(m[2]), - StartCol: toInt(m[3]), - EndLine: toInt(m[4]), - EndCol: toInt(m[5]), - NumStmt: toInt(m[6]), - Count: toInt(m[7]), - }) - } - if err := s.Err(); err != nil { - return nil, err - } - for _, p := range files { - sort.Sort(blocksByStart(p.Blocks)) - } - // Generate a sorted slice. - profiles := make([]*Profile, 0, len(files)) - for _, profile := range files { - profiles = append(profiles, profile) - } - sort.Sort(byFileName(profiles)) - return profiles, nil -} - -type blocksByStart []ProfileBlock - -func (b blocksByStart) Len() int { return len(b) } -func (b blocksByStart) Swap(i, j int) { b[i], b[j] = b[j], b[i] } -func (b blocksByStart) Less(i, j int) bool { - bi, bj := b[i], b[j] - return bi.StartLine < bj.StartLine || bi.StartLine == bj.StartLine && bi.StartCol < bj.StartCol -} - -var lineRe = regexp.MustCompile(`^(.+):([0-9]+).([0-9]+),([0-9]+).([0-9]+) ([0-9]+) ([0-9]+)$`) - -func toInt(s string) int { - i, err := strconv.Atoi(s) - if err != nil { - panic(err) - } - return i -} - -// Boundary represents the position in a source file of the beginning or end of a -// block as reported by the coverage profile. In HTML mode, it will correspond to -// the opening or closing of a tag and will be used to colorize the source -type Boundary struct { - Offset int // Location as a byte offset in the source file. - Start bool // Is this the start of a block? - Count int // Event count from the cover profile. - Norm float64 // Count normalized to [0..1]. -} - -// Boundaries returns a Profile as a set of Boundary objects within the provided src. -func (p *Profile) Boundaries(src []byte) (boundaries []Boundary) { - // Find maximum count. - max := 0 - for _, b := range p.Blocks { - if b.Count > max { - max = b.Count - } - } - // Divisor for normalization. - divisor := math.Log(float64(max)) - - // boundary returns a Boundary, populating the Norm field with a normalized Count. - boundary := func(offset int, start bool, count int) Boundary { - b := Boundary{Offset: offset, Start: start, Count: count} - if !start || count == 0 { - return b - } - if max <= 1 { - b.Norm = 0.8 // Profile is in"set" mode; we want a heat map. Use cov8 in the CSS. - } else if count > 0 { - b.Norm = math.Log(float64(count)) / divisor - } - return b - } - - line, col := 1, 2 // TODO: Why is this 2? - for si, bi := 0, 0; si < len(src) && bi < len(p.Blocks); { - b := p.Blocks[bi] - if b.StartLine == line && b.StartCol == col { - boundaries = append(boundaries, boundary(si, true, b.Count)) - } - if b.EndLine == line && b.EndCol == col { - boundaries = append(boundaries, boundary(si, false, 0)) - bi++ - continue // Don't advance through src; maybe the next block starts here. - } - if src[si] == '\n' { - line++ - col = 0 - } - col++ - si++ - } - sort.Sort(boundariesByPos(boundaries)) - return -} - -type boundariesByPos []Boundary - -func (b boundariesByPos) Len() int { return len(b) } -func (b boundariesByPos) Swap(i, j int) { b[i], b[j] = b[j], b[i] } -func (b boundariesByPos) Less(i, j int) bool { - if b[i].Offset == b[j].Offset { - return !b[i].Start && b[j].Start - } - return b[i].Offset < b[j].Offset -} - -// findFile finds the location of the named file in GOROOT, GOPATH etc. -func findFile(file string) (string, error) { - if strings.HasPrefix(file, "_") { - file = file[1:] - } - if _, err := os.Stat(file); err == nil { - return file, nil - } - dir, file := filepath.Split(file) - pkg, err := build.Import(dir, ".", build.FindOnly) - if err != nil { - return "", fmt.Errorf("can't find %q: %v", file, err) - } - return filepath.Join(pkg.Dir, file), nil -} diff --git a/tools/vendor/github.com/tetafro/godot/.golangci.yml b/tools/vendor/github.com/tetafro/godot/.golangci.yml index ea380eb83d..b9e4435810 100644 --- a/tools/vendor/github.com/tetafro/godot/.golangci.yml +++ b/tools/vendor/github.com/tetafro/godot/.golangci.yml @@ -1,9 +1,6 @@ run: concurrency: 2 - deadline: 5m - -skip-dirs: - - path: ./testdata/ + timeout: 5m linters: disable-all: true @@ -13,6 +10,7 @@ linters: - cyclop - dogsled - durationcheck + - err113 - errcheck - errname - errorlint @@ -25,7 +23,6 @@ linters: - gocritic - gocyclo - godot - - goerr113 - gofmt - gofumpt - goimports @@ -64,6 +61,8 @@ issues: exclude-use-default: false exclude: - "do not define dynamic errors, use wrapped static errors instead" + exclude-files: + - ./testdata/ exclude-rules: - path: _test\.go linters: diff --git a/tools/vendor/github.com/tetafro/godot/checks.go b/tools/vendor/github.com/tetafro/godot/checks.go index 0e53c220a2..df5019f6c8 100644 --- a/tools/vendor/github.com/tetafro/godot/checks.go +++ b/tools/vendor/github.com/tetafro/godot/checks.go @@ -171,7 +171,7 @@ func checkCapital(c comment) []Issue { if state == endOfSentence && unicode.IsLower(r) { pp = append(pp, position{ line: pos.line, - column: runeToByteColumn(c.text, pos.column), + column: runeToByteColumn(c.lines[pos.line-1], pos.column), }) } state = empty @@ -230,7 +230,8 @@ func isSpecialBlock(comment string) bool { strings.Contains(comment, "#define")) { return true } - if strings.HasPrefix(comment, "// Output: ") { + if strings.HasPrefix(comment, "// Output:") || + strings.HasPrefix(comment, "// Unordered output:") { return true } return false diff --git a/tools/vendor/github.com/tomarrell/wrapcheck/v2/wrapcheck/wrapcheck.go b/tools/vendor/github.com/tomarrell/wrapcheck/v2/wrapcheck/wrapcheck.go index 79e7bba863..2c1b2d20be 100644 --- a/tools/vendor/github.com/tomarrell/wrapcheck/v2/wrapcheck/wrapcheck.go +++ b/tools/vendor/github.com/tomarrell/wrapcheck/v2/wrapcheck/wrapcheck.go @@ -283,9 +283,9 @@ func reportUnwrapped( } // Check if the underlying type of the "x" in x.y.z is an interface, as - // errors returned from interface types should be wrapped, unless ignored - // as per `ignoreInterfaceRegexps` - if isInterface(pass, sel) { + // errors returned from exported interface types should be wrapped, unless + // ignored as per `ignoreInterfaceRegexps` + if sel.Sel.IsExported() && isInterface(pass, sel) { pkgPath := pass.TypesInfo.ObjectOf(sel.Sel).Pkg().Path() name := types.TypeString(pass.TypesInfo.TypeOf(sel.X), func(p *types.Package) string { return p.Name() }) if !containsMatch(regexpsInter, name) && !containsMatchGlob(pkgGlobs, pkgPath) { diff --git a/tools/vendor/github.com/uudashr/gocognit/README.md b/tools/vendor/github.com/uudashr/gocognit/README.md index 57f31cf740..c15f61bcfc 100644 --- a/tools/vendor/github.com/uudashr/gocognit/README.md +++ b/tools/vendor/github.com/uudashr/gocognit/README.md @@ -1,4 +1,6 @@ -[![GoDoc](https://godoc.org/github.com/uudashr/gocognit?status.svg)](https://godoc.org/github.com/uudashr/gocognit) +[![Go Reference](https://pkg.go.dev/badge/github.com/uudashr/gocognit.svg)](https://pkg.go.dev/github.com/uudashr/gocognit) +[![go-recipes](https://raw.githubusercontent.com/nikolaydubina/go-recipes/main/badge.svg?raw=true)](https://github.com/nikolaydubina/go-recipes) + # Gocognit Gocognit calculates cognitive complexities of functions (and methods) in Go source code. A measurement of how hard does the code is intuitively to understand. @@ -223,4 +225,4 @@ func IgnoreMe() { ## Related project - [Gocyclo](https://github.com/fzipp/gocyclo) where the code are based on. -- [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf) white paper by G. Ann Campbell. \ No newline at end of file +- [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf) white paper by G. Ann Campbell. diff --git a/tools/vendor/go-simpler.org/sloglint/sloglint.go b/tools/vendor/go-simpler.org/sloglint/sloglint.go index ee6e5e8c0a..bff37b7236 100644 --- a/tools/vendor/go-simpler.org/sloglint/sloglint.go +++ b/tools/vendor/go-simpler.org/sloglint/sloglint.go @@ -242,21 +242,24 @@ func visit(pass *analysis.Pass, opts *Options, node ast.Node, stack []ast.Node) if typ == nil { continue } + switch typ.String() { case "string": keys = append(keys, args[i]) i++ // skip the value. case "log/slog.Attr": attrs = append(attrs, args[i]) + case "[]any", "[]log/slog.Attr": + continue // the last argument may be an unpacked slice, skip it. } } switch { case opts.KVOnly && len(attrs) > 0: pass.Reportf(call.Pos(), "attributes should not be used") - case opts.AttrOnly && len(attrs) < len(args): + case opts.AttrOnly && len(keys) > 0: pass.Reportf(call.Pos(), "key-value pairs should not be used") - case opts.NoMixedArgs && 0 < len(attrs) && len(attrs) < len(args): + case opts.NoMixedArgs && len(attrs) > 0 && len(keys) > 0: pass.Reportf(call.Pos(), "key-value pairs and attributes should not be mixed") } diff --git a/tools/vendor/golang.org/x/exp/LICENSE b/tools/vendor/golang.org/x/exp/LICENSE index 6a66aea5ea..2a7cf70da6 100644 --- a/tools/vendor/golang.org/x/exp/LICENSE +++ b/tools/vendor/golang.org/x/exp/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. +Copyright 2009 The Go Authors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -10,7 +10,7 @@ notice, this list of conditions and the following disclaimer. copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Google Inc. nor the names of its + * Neither the name of Google LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/tools/vendor/golang.org/x/exp/slices/slices.go b/tools/vendor/golang.org/x/exp/slices/slices.go index 5e8158bba8..46ceac3439 100644 --- a/tools/vendor/golang.org/x/exp/slices/slices.go +++ b/tools/vendor/golang.org/x/exp/slices/slices.go @@ -209,25 +209,37 @@ func Insert[S ~[]E, E any](s S, i int, v ...E) S { return s } +// clearSlice sets all elements up to the length of s to the zero value of E. +// We may use the builtin clear func instead, and remove clearSlice, when upgrading +// to Go 1.21+. +func clearSlice[S ~[]E, E any](s S) { + var zero E + for i := range s { + s[i] = zero + } +} + // Delete removes the elements s[i:j] from s, returning the modified slice. -// Delete panics if s[i:j] is not a valid slice of s. -// Delete is O(len(s)-j), so if many items must be deleted, it is better to +// Delete panics if j > len(s) or s[i:j] is not a valid slice of s. +// Delete is O(len(s)-i), so if many items must be deleted, it is better to // make a single call deleting them all together than to delete one at a time. -// Delete might not modify the elements s[len(s)-(j-i):len(s)]. If those -// elements contain pointers you might consider zeroing those elements so that -// objects they reference can be garbage collected. +// Delete zeroes the elements s[len(s)-(j-i):len(s)]. func Delete[S ~[]E, E any](s S, i, j int) S { - _ = s[i:j] // bounds check + _ = s[i:j:len(s)] // bounds check - return append(s[:i], s[j:]...) + if i == j { + return s + } + + oldlen := len(s) + s = append(s[:i], s[j:]...) + clearSlice(s[len(s):oldlen]) // zero/nil out the obsolete elements, for GC + return s } // DeleteFunc removes any elements from s for which del returns true, // returning the modified slice. -// When DeleteFunc removes m elements, it might not modify the elements -// s[len(s)-m:len(s)]. If those elements contain pointers you might consider -// zeroing those elements so that objects they reference can be garbage -// collected. +// DeleteFunc zeroes the elements between the new length and the original length. func DeleteFunc[S ~[]E, E any](s S, del func(E) bool) S { i := IndexFunc(s, del) if i == -1 { @@ -240,11 +252,13 @@ func DeleteFunc[S ~[]E, E any](s S, del func(E) bool) S { i++ } } + clearSlice(s[i:]) // zero/nil out the obsolete elements, for GC return s[:i] } // Replace replaces the elements s[i:j] by the given v, and returns the // modified slice. Replace panics if s[i:j] is not a valid slice of s. +// When len(v) < (j-i), Replace zeroes the elements between the new length and the original length. func Replace[S ~[]E, E any](s S, i, j int, v ...E) S { _ = s[i:j] // verify that i:j is a valid subslice @@ -272,6 +286,7 @@ func Replace[S ~[]E, E any](s S, i, j int, v ...E) S { if i+len(v) != j { copy(r[i+len(v):], s[j:]) } + clearSlice(s[tot:]) // zero/nil out the obsolete elements, for GC return r } @@ -345,9 +360,7 @@ func Clone[S ~[]E, E any](s S) S { // This is like the uniq command found on Unix. // Compact modifies the contents of the slice s and returns the modified slice, // which may have a smaller length. -// When Compact discards m elements in total, it might not modify the elements -// s[len(s)-m:len(s)]. If those elements contain pointers you might consider -// zeroing those elements so that objects they reference can be garbage collected. +// Compact zeroes the elements between the new length and the original length. func Compact[S ~[]E, E comparable](s S) S { if len(s) < 2 { return s @@ -361,11 +374,13 @@ func Compact[S ~[]E, E comparable](s S) S { i++ } } + clearSlice(s[i:]) // zero/nil out the obsolete elements, for GC return s[:i] } // CompactFunc is like [Compact] but uses an equality function to compare elements. // For runs of elements that compare equal, CompactFunc keeps the first one. +// CompactFunc zeroes the elements between the new length and the original length. func CompactFunc[S ~[]E, E any](s S, eq func(E, E) bool) S { if len(s) < 2 { return s @@ -379,6 +394,7 @@ func CompactFunc[S ~[]E, E any](s S, eq func(E, E) bool) S { i++ } } + clearSlice(s[i:]) // zero/nil out the obsolete elements, for GC return s[:i] } diff --git a/tools/vendor/golang.org/x/exp/slices/sort.go b/tools/vendor/golang.org/x/exp/slices/sort.go index b67897f76b..f58bbc7ba4 100644 --- a/tools/vendor/golang.org/x/exp/slices/sort.go +++ b/tools/vendor/golang.org/x/exp/slices/sort.go @@ -22,10 +22,12 @@ func Sort[S ~[]E, E constraints.Ordered](x S) { // SortFunc sorts the slice x in ascending order as determined by the cmp // function. This sort is not guaranteed to be stable. // cmp(a, b) should return a negative number when a < b, a positive number when -// a > b and zero when a == b. +// a > b and zero when a == b or when a is not comparable to b in the sense +// of the formal definition of Strict Weak Ordering. // // SortFunc requires that cmp is a strict weak ordering. // See https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings. +// To indicate 'uncomparable', return 0 from the function. func SortFunc[S ~[]E, E any](x S, cmp func(a, b E) int) { n := len(x) pdqsortCmpFunc(x, 0, n, bits.Len(uint(n)), cmp) diff --git a/tools/vendor/golang.org/x/sys/unix/mkerrors.sh b/tools/vendor/golang.org/x/sys/unix/mkerrors.sh index d07dd09eb5..e14b766a32 100644 --- a/tools/vendor/golang.org/x/sys/unix/mkerrors.sh +++ b/tools/vendor/golang.org/x/sys/unix/mkerrors.sh @@ -552,6 +552,7 @@ ccflags="$@" $2 !~ /^RTC_VL_(ACCURACY|BACKUP|DATA)/ && $2 ~ /^(NETLINK|NLM|NLMSG|NLA|IFA|IFAN|RT|RTC|RTCF|RTN|RTPROT|RTNH|ARPHRD|ETH_P|NETNSA)_/ || $2 ~ /^SOCK_|SK_DIAG_|SKNLGRP_$/ || + $2 ~ /^(CONNECT|SAE)_/ || $2 ~ /^FIORDCHK$/ || $2 ~ /^SIOC/ || $2 ~ /^TIOC/ || diff --git a/tools/vendor/golang.org/x/sys/unix/syscall_darwin.go b/tools/vendor/golang.org/x/sys/unix/syscall_darwin.go index 2d15200adb..099867deed 100644 --- a/tools/vendor/golang.org/x/sys/unix/syscall_darwin.go +++ b/tools/vendor/golang.org/x/sys/unix/syscall_darwin.go @@ -566,6 +566,43 @@ func PthreadFchdir(fd int) (err error) { return pthread_fchdir_np(fd) } +// Connectx calls connectx(2) to initiate a connection on a socket. +// +// srcIf, srcAddr, and dstAddr are filled into a [SaEndpoints] struct and passed as the endpoints argument. +// +// - srcIf is the optional source interface index. 0 means unspecified. +// - srcAddr is the optional source address. nil means unspecified. +// - dstAddr is the destination address. +// +// On success, Connectx returns the number of bytes enqueued for transmission. +func Connectx(fd int, srcIf uint32, srcAddr, dstAddr Sockaddr, associd SaeAssocID, flags uint32, iov []Iovec, connid *SaeConnID) (n uintptr, err error) { + endpoints := SaEndpoints{ + Srcif: srcIf, + } + + if srcAddr != nil { + addrp, addrlen, err := srcAddr.sockaddr() + if err != nil { + return 0, err + } + endpoints.Srcaddr = (*RawSockaddr)(addrp) + endpoints.Srcaddrlen = uint32(addrlen) + } + + if dstAddr != nil { + addrp, addrlen, err := dstAddr.sockaddr() + if err != nil { + return 0, err + } + endpoints.Dstaddr = (*RawSockaddr)(addrp) + endpoints.Dstaddrlen = uint32(addrlen) + } + + err = connectx(fd, &endpoints, associd, flags, iov, &n, connid) + return +} + +//sys connectx(fd int, endpoints *SaEndpoints, associd SaeAssocID, flags uint32, iov []Iovec, n *uintptr, connid *SaeConnID) (err error) //sys sendfile(infd int, outfd int, offset int64, len *int64, hdtr unsafe.Pointer, flags int) (err error) //sys shmat(id int, addr uintptr, flag int) (ret uintptr, err error) diff --git a/tools/vendor/golang.org/x/sys/unix/syscall_hurd.go b/tools/vendor/golang.org/x/sys/unix/syscall_hurd.go index ba46651f8e..a6a2d2fc2b 100644 --- a/tools/vendor/golang.org/x/sys/unix/syscall_hurd.go +++ b/tools/vendor/golang.org/x/sys/unix/syscall_hurd.go @@ -11,6 +11,7 @@ package unix int ioctl(int, unsigned long int, uintptr_t); */ import "C" +import "unsafe" func ioctl(fd int, req uint, arg uintptr) (err error) { r0, er := C.ioctl(C.int(fd), C.ulong(req), C.uintptr_t(arg)) diff --git a/tools/vendor/golang.org/x/sys/unix/zerrors_darwin_amd64.go b/tools/vendor/golang.org/x/sys/unix/zerrors_darwin_amd64.go index 4308ac1772..d73c4652e6 100644 --- a/tools/vendor/golang.org/x/sys/unix/zerrors_darwin_amd64.go +++ b/tools/vendor/golang.org/x/sys/unix/zerrors_darwin_amd64.go @@ -237,6 +237,9 @@ const ( CLOCK_UPTIME_RAW_APPROX = 0x9 CLONE_NOFOLLOW = 0x1 CLONE_NOOWNERCOPY = 0x2 + CONNECT_DATA_AUTHENTICATED = 0x4 + CONNECT_DATA_IDEMPOTENT = 0x2 + CONNECT_RESUME_ON_READ_WRITE = 0x1 CR0 = 0x0 CR1 = 0x1000 CR2 = 0x2000 @@ -1265,6 +1268,10 @@ const ( RTV_SSTHRESH = 0x20 RUSAGE_CHILDREN = -0x1 RUSAGE_SELF = 0x0 + SAE_ASSOCID_ALL = 0xffffffff + SAE_ASSOCID_ANY = 0x0 + SAE_CONNID_ALL = 0xffffffff + SAE_CONNID_ANY = 0x0 SCM_CREDS = 0x3 SCM_RIGHTS = 0x1 SCM_TIMESTAMP = 0x2 diff --git a/tools/vendor/golang.org/x/sys/unix/zerrors_darwin_arm64.go b/tools/vendor/golang.org/x/sys/unix/zerrors_darwin_arm64.go index c8068a7a16..4a55a40058 100644 --- a/tools/vendor/golang.org/x/sys/unix/zerrors_darwin_arm64.go +++ b/tools/vendor/golang.org/x/sys/unix/zerrors_darwin_arm64.go @@ -237,6 +237,9 @@ const ( CLOCK_UPTIME_RAW_APPROX = 0x9 CLONE_NOFOLLOW = 0x1 CLONE_NOOWNERCOPY = 0x2 + CONNECT_DATA_AUTHENTICATED = 0x4 + CONNECT_DATA_IDEMPOTENT = 0x2 + CONNECT_RESUME_ON_READ_WRITE = 0x1 CR0 = 0x0 CR1 = 0x1000 CR2 = 0x2000 @@ -1265,6 +1268,10 @@ const ( RTV_SSTHRESH = 0x20 RUSAGE_CHILDREN = -0x1 RUSAGE_SELF = 0x0 + SAE_ASSOCID_ALL = 0xffffffff + SAE_ASSOCID_ANY = 0x0 + SAE_CONNID_ALL = 0xffffffff + SAE_CONNID_ANY = 0x0 SCM_CREDS = 0x3 SCM_RIGHTS = 0x1 SCM_TIMESTAMP = 0x2 diff --git a/tools/vendor/golang.org/x/sys/unix/zerrors_zos_s390x.go b/tools/vendor/golang.org/x/sys/unix/zerrors_zos_s390x.go index da08b2ab3d..1ec2b1407b 100644 --- a/tools/vendor/golang.org/x/sys/unix/zerrors_zos_s390x.go +++ b/tools/vendor/golang.org/x/sys/unix/zerrors_zos_s390x.go @@ -581,6 +581,8 @@ const ( AT_EMPTY_PATH = 0x1000 AT_REMOVEDIR = 0x200 RENAME_NOREPLACE = 1 << 0 + ST_RDONLY = 1 + ST_NOSUID = 2 ) const ( diff --git a/tools/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go b/tools/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go index b622533ef2..24b346e1a3 100644 --- a/tools/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go +++ b/tools/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go @@ -841,6 +841,26 @@ var libc_pthread_fchdir_np_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func connectx(fd int, endpoints *SaEndpoints, associd SaeAssocID, flags uint32, iov []Iovec, n *uintptr, connid *SaeConnID) (err error) { + var _p0 unsafe.Pointer + if len(iov) > 0 { + _p0 = unsafe.Pointer(&iov[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + _, _, e1 := syscall_syscall9(libc_connectx_trampoline_addr, uintptr(fd), uintptr(unsafe.Pointer(endpoints)), uintptr(associd), uintptr(flags), uintptr(_p0), uintptr(len(iov)), uintptr(unsafe.Pointer(n)), uintptr(unsafe.Pointer(connid)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_connectx_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_connectx connectx "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func sendfile(infd int, outfd int, offset int64, len *int64, hdtr unsafe.Pointer, flags int) (err error) { _, _, e1 := syscall_syscall6(libc_sendfile_trampoline_addr, uintptr(infd), uintptr(outfd), uintptr(offset), uintptr(unsafe.Pointer(len)), uintptr(hdtr), uintptr(flags)) if e1 != 0 { diff --git a/tools/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s b/tools/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s index cfe6646baf..ebd213100b 100644 --- a/tools/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s +++ b/tools/vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s @@ -248,6 +248,11 @@ TEXT libc_pthread_fchdir_np_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_pthread_fchdir_np_trampoline_addr(SB), RODATA, $8 DATA ·libc_pthread_fchdir_np_trampoline_addr(SB)/8, $libc_pthread_fchdir_np_trampoline<>(SB) +TEXT libc_connectx_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_connectx(SB) +GLOBL ·libc_connectx_trampoline_addr(SB), RODATA, $8 +DATA ·libc_connectx_trampoline_addr(SB)/8, $libc_connectx_trampoline<>(SB) + TEXT libc_sendfile_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_sendfile(SB) GLOBL ·libc_sendfile_trampoline_addr(SB), RODATA, $8 diff --git a/tools/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go b/tools/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go index 13f624f69f..824b9c2d5e 100644 --- a/tools/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go +++ b/tools/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.go @@ -841,6 +841,26 @@ var libc_pthread_fchdir_np_trampoline_addr uintptr // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func connectx(fd int, endpoints *SaEndpoints, associd SaeAssocID, flags uint32, iov []Iovec, n *uintptr, connid *SaeConnID) (err error) { + var _p0 unsafe.Pointer + if len(iov) > 0 { + _p0 = unsafe.Pointer(&iov[0]) + } else { + _p0 = unsafe.Pointer(&_zero) + } + _, _, e1 := syscall_syscall9(libc_connectx_trampoline_addr, uintptr(fd), uintptr(unsafe.Pointer(endpoints)), uintptr(associd), uintptr(flags), uintptr(_p0), uintptr(len(iov)), uintptr(unsafe.Pointer(n)), uintptr(unsafe.Pointer(connid)), 0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +var libc_connectx_trampoline_addr uintptr + +//go:cgo_import_dynamic libc_connectx connectx "/usr/lib/libSystem.B.dylib" + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func sendfile(infd int, outfd int, offset int64, len *int64, hdtr unsafe.Pointer, flags int) (err error) { _, _, e1 := syscall_syscall6(libc_sendfile_trampoline_addr, uintptr(infd), uintptr(outfd), uintptr(offset), uintptr(unsafe.Pointer(len)), uintptr(hdtr), uintptr(flags)) if e1 != 0 { diff --git a/tools/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s b/tools/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s index fe222b75df..4f178a2293 100644 --- a/tools/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s +++ b/tools/vendor/golang.org/x/sys/unix/zsyscall_darwin_arm64.s @@ -248,6 +248,11 @@ TEXT libc_pthread_fchdir_np_trampoline<>(SB),NOSPLIT,$0-0 GLOBL ·libc_pthread_fchdir_np_trampoline_addr(SB), RODATA, $8 DATA ·libc_pthread_fchdir_np_trampoline_addr(SB)/8, $libc_pthread_fchdir_np_trampoline<>(SB) +TEXT libc_connectx_trampoline<>(SB),NOSPLIT,$0-0 + JMP libc_connectx(SB) +GLOBL ·libc_connectx_trampoline_addr(SB), RODATA, $8 +DATA ·libc_connectx_trampoline_addr(SB)/8, $libc_connectx_trampoline<>(SB) + TEXT libc_sendfile_trampoline<>(SB),NOSPLIT,$0-0 JMP libc_sendfile(SB) GLOBL ·libc_sendfile_trampoline_addr(SB), RODATA, $8 diff --git a/tools/vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go b/tools/vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go index 091d107f3a..d003c3d437 100644 --- a/tools/vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go +++ b/tools/vendor/golang.org/x/sys/unix/ztypes_darwin_amd64.go @@ -306,6 +306,19 @@ type XVSockPgen struct { type _Socklen uint32 +type SaeAssocID uint32 + +type SaeConnID uint32 + +type SaEndpoints struct { + Srcif uint32 + Srcaddr *RawSockaddr + Srcaddrlen uint32 + Dstaddr *RawSockaddr + Dstaddrlen uint32 + _ [4]byte +} + type Xucred struct { Version uint32 Uid uint32 diff --git a/tools/vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go b/tools/vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go index 28ff4ef74d..0d45a941aa 100644 --- a/tools/vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go +++ b/tools/vendor/golang.org/x/sys/unix/ztypes_darwin_arm64.go @@ -306,6 +306,19 @@ type XVSockPgen struct { type _Socklen uint32 +type SaeAssocID uint32 + +type SaeConnID uint32 + +type SaEndpoints struct { + Srcif uint32 + Srcaddr *RawSockaddr + Srcaddrlen uint32 + Dstaddr *RawSockaddr + Dstaddrlen uint32 + _ [4]byte +} + type Xucred struct { Version uint32 Uid uint32 diff --git a/tools/vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go b/tools/vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go index 6cbd094a3a..51e13eb055 100644 --- a/tools/vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go +++ b/tools/vendor/golang.org/x/sys/unix/ztypes_freebsd_386.go @@ -625,6 +625,7 @@ const ( POLLRDNORM = 0x40 POLLWRBAND = 0x100 POLLWRNORM = 0x4 + POLLRDHUP = 0x4000 ) type CapRights struct { diff --git a/tools/vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go b/tools/vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go index 7c03b6ee77..d002d8ef3c 100644 --- a/tools/vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go +++ b/tools/vendor/golang.org/x/sys/unix/ztypes_freebsd_amd64.go @@ -630,6 +630,7 @@ const ( POLLRDNORM = 0x40 POLLWRBAND = 0x100 POLLWRNORM = 0x4 + POLLRDHUP = 0x4000 ) type CapRights struct { diff --git a/tools/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go b/tools/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go index 422107ee8b..3f863d898d 100644 --- a/tools/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go +++ b/tools/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm.go @@ -616,6 +616,7 @@ const ( POLLRDNORM = 0x40 POLLWRBAND = 0x100 POLLWRNORM = 0x4 + POLLRDHUP = 0x4000 ) type CapRights struct { diff --git a/tools/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm64.go b/tools/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm64.go index 505a12acfd..61c7293106 100644 --- a/tools/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm64.go +++ b/tools/vendor/golang.org/x/sys/unix/ztypes_freebsd_arm64.go @@ -610,6 +610,7 @@ const ( POLLRDNORM = 0x40 POLLWRBAND = 0x100 POLLWRNORM = 0x4 + POLLRDHUP = 0x4000 ) type CapRights struct { diff --git a/tools/vendor/golang.org/x/sys/unix/ztypes_freebsd_riscv64.go b/tools/vendor/golang.org/x/sys/unix/ztypes_freebsd_riscv64.go index cc986c7900..b5d17414f0 100644 --- a/tools/vendor/golang.org/x/sys/unix/ztypes_freebsd_riscv64.go +++ b/tools/vendor/golang.org/x/sys/unix/ztypes_freebsd_riscv64.go @@ -612,6 +612,7 @@ const ( POLLRDNORM = 0x40 POLLWRBAND = 0x100 POLLWRNORM = 0x4 + POLLRDHUP = 0x4000 ) type CapRights struct { diff --git a/tools/vendor/golang.org/x/sys/unix/ztypes_linux.go b/tools/vendor/golang.org/x/sys/unix/ztypes_linux.go index b102b95a0a..9f2550dc31 100644 --- a/tools/vendor/golang.org/x/sys/unix/ztypes_linux.go +++ b/tools/vendor/golang.org/x/sys/unix/ztypes_linux.go @@ -2486,7 +2486,7 @@ type XDPMmapOffsets struct { type XDPUmemReg struct { Addr uint64 Len uint64 - Chunk_size uint32 + Size uint32 Headroom uint32 Flags uint32 Tx_metadata_len uint32 @@ -3807,6 +3807,9 @@ const ( ETHTOOL_MSG_PSE_GET_REPLY = 0x25 ETHTOOL_MSG_RSS_GET_REPLY = 0x26 ETHTOOL_MSG_KERNEL_MAX = 0x2b + ETHTOOL_FLAG_COMPACT_BITSETS = 0x1 + ETHTOOL_FLAG_OMIT_REPLY = 0x2 + ETHTOOL_FLAG_STATS = 0x4 ETHTOOL_A_HEADER_UNSPEC = 0x0 ETHTOOL_A_HEADER_DEV_INDEX = 0x1 ETHTOOL_A_HEADER_DEV_NAME = 0x2 diff --git a/tools/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go b/tools/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go index 15adc04142..ad05b51a60 100644 --- a/tools/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go +++ b/tools/vendor/golang.org/x/sys/unix/ztypes_linux_riscv64.go @@ -727,6 +727,37 @@ const ( RISCV_HWPROBE_EXT_ZBA = 0x8 RISCV_HWPROBE_EXT_ZBB = 0x10 RISCV_HWPROBE_EXT_ZBS = 0x20 + RISCV_HWPROBE_EXT_ZICBOZ = 0x40 + RISCV_HWPROBE_EXT_ZBC = 0x80 + RISCV_HWPROBE_EXT_ZBKB = 0x100 + RISCV_HWPROBE_EXT_ZBKC = 0x200 + RISCV_HWPROBE_EXT_ZBKX = 0x400 + RISCV_HWPROBE_EXT_ZKND = 0x800 + RISCV_HWPROBE_EXT_ZKNE = 0x1000 + RISCV_HWPROBE_EXT_ZKNH = 0x2000 + RISCV_HWPROBE_EXT_ZKSED = 0x4000 + RISCV_HWPROBE_EXT_ZKSH = 0x8000 + RISCV_HWPROBE_EXT_ZKT = 0x10000 + RISCV_HWPROBE_EXT_ZVBB = 0x20000 + RISCV_HWPROBE_EXT_ZVBC = 0x40000 + RISCV_HWPROBE_EXT_ZVKB = 0x80000 + RISCV_HWPROBE_EXT_ZVKG = 0x100000 + RISCV_HWPROBE_EXT_ZVKNED = 0x200000 + RISCV_HWPROBE_EXT_ZVKNHA = 0x400000 + RISCV_HWPROBE_EXT_ZVKNHB = 0x800000 + RISCV_HWPROBE_EXT_ZVKSED = 0x1000000 + RISCV_HWPROBE_EXT_ZVKSH = 0x2000000 + RISCV_HWPROBE_EXT_ZVKT = 0x4000000 + RISCV_HWPROBE_EXT_ZFH = 0x8000000 + RISCV_HWPROBE_EXT_ZFHMIN = 0x10000000 + RISCV_HWPROBE_EXT_ZIHINTNTL = 0x20000000 + RISCV_HWPROBE_EXT_ZVFH = 0x40000000 + RISCV_HWPROBE_EXT_ZVFHMIN = 0x80000000 + RISCV_HWPROBE_EXT_ZFA = 0x100000000 + RISCV_HWPROBE_EXT_ZTSO = 0x200000000 + RISCV_HWPROBE_EXT_ZACAS = 0x400000000 + RISCV_HWPROBE_EXT_ZICOND = 0x800000000 + RISCV_HWPROBE_EXT_ZIHINTPAUSE = 0x1000000000 RISCV_HWPROBE_KEY_CPUPERF_0 = 0x5 RISCV_HWPROBE_MISALIGNED_UNKNOWN = 0x0 RISCV_HWPROBE_MISALIGNED_EMULATED = 0x1 @@ -734,4 +765,6 @@ const ( RISCV_HWPROBE_MISALIGNED_FAST = 0x3 RISCV_HWPROBE_MISALIGNED_UNSUPPORTED = 0x4 RISCV_HWPROBE_MISALIGNED_MASK = 0x7 + RISCV_HWPROBE_KEY_ZICBOZ_BLOCK_SIZE = 0x6 + RISCV_HWPROBE_WHICH_CPUS = 0x1 ) diff --git a/tools/vendor/golang.org/x/sys/windows/syscall_windows.go b/tools/vendor/golang.org/x/sys/windows/syscall_windows.go index 1fa34fd17c..5cee9a3143 100644 --- a/tools/vendor/golang.org/x/sys/windows/syscall_windows.go +++ b/tools/vendor/golang.org/x/sys/windows/syscall_windows.go @@ -313,6 +313,10 @@ func NewCallbackCDecl(fn interface{}) uintptr { //sys SetConsoleMode(console Handle, mode uint32) (err error) = kernel32.SetConsoleMode //sys GetConsoleScreenBufferInfo(console Handle, info *ConsoleScreenBufferInfo) (err error) = kernel32.GetConsoleScreenBufferInfo //sys setConsoleCursorPosition(console Handle, position uint32) (err error) = kernel32.SetConsoleCursorPosition +//sys GetConsoleCP() (cp uint32, err error) = kernel32.GetConsoleCP +//sys GetConsoleOutputCP() (cp uint32, err error) = kernel32.GetConsoleOutputCP +//sys SetConsoleCP(cp uint32) (err error) = kernel32.SetConsoleCP +//sys SetConsoleOutputCP(cp uint32) (err error) = kernel32.SetConsoleOutputCP //sys WriteConsole(console Handle, buf *uint16, towrite uint32, written *uint32, reserved *byte) (err error) = kernel32.WriteConsoleW //sys ReadConsole(console Handle, buf *uint16, toread uint32, read *uint32, inputControl *byte) (err error) = kernel32.ReadConsoleW //sys resizePseudoConsole(pconsole Handle, size uint32) (hr error) = kernel32.ResizePseudoConsole diff --git a/tools/vendor/golang.org/x/sys/windows/types_windows.go b/tools/vendor/golang.org/x/sys/windows/types_windows.go index 4d0c15745f..7b97a154c9 100644 --- a/tools/vendor/golang.org/x/sys/windows/types_windows.go +++ b/tools/vendor/golang.org/x/sys/windows/types_windows.go @@ -1060,6 +1060,7 @@ const ( SIO_GET_EXTENSION_FUNCTION_POINTER = IOC_INOUT | IOC_WS2 | 6 SIO_KEEPALIVE_VALS = IOC_IN | IOC_VENDOR | 4 SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12 + SIO_UDP_NETRESET = IOC_IN | IOC_VENDOR | 15 // cf. http://support.microsoft.com/default.aspx?scid=kb;en-us;257460 @@ -2031,6 +2032,50 @@ const ( IF_TYPE_IEEE1394 = 144 ) +// Enum NL_PREFIX_ORIGIN for [IpAdapterUnicastAddress], see +// https://learn.microsoft.com/en-us/windows/win32/api/nldef/ne-nldef-nl_prefix_origin +const ( + IpPrefixOriginOther = 0 + IpPrefixOriginManual = 1 + IpPrefixOriginWellKnown = 2 + IpPrefixOriginDhcp = 3 + IpPrefixOriginRouterAdvertisement = 4 + IpPrefixOriginUnchanged = 1 << 4 +) + +// Enum NL_SUFFIX_ORIGIN for [IpAdapterUnicastAddress], see +// https://learn.microsoft.com/en-us/windows/win32/api/nldef/ne-nldef-nl_suffix_origin +const ( + NlsoOther = 0 + NlsoManual = 1 + NlsoWellKnown = 2 + NlsoDhcp = 3 + NlsoLinkLayerAddress = 4 + NlsoRandom = 5 + IpSuffixOriginOther = 0 + IpSuffixOriginManual = 1 + IpSuffixOriginWellKnown = 2 + IpSuffixOriginDhcp = 3 + IpSuffixOriginLinkLayerAddress = 4 + IpSuffixOriginRandom = 5 + IpSuffixOriginUnchanged = 1 << 4 +) + +// Enum NL_DAD_STATE for [IpAdapterUnicastAddress], see +// https://learn.microsoft.com/en-us/windows/win32/api/nldef/ne-nldef-nl_dad_state +const ( + NldsInvalid = 0 + NldsTentative = 1 + NldsDuplicate = 2 + NldsDeprecated = 3 + NldsPreferred = 4 + IpDadStateInvalid = 0 + IpDadStateTentative = 1 + IpDadStateDuplicate = 2 + IpDadStateDeprecated = 3 + IpDadStatePreferred = 4 +) + type SocketAddress struct { Sockaddr *syscall.RawSockaddrAny SockaddrLength int32 diff --git a/tools/vendor/golang.org/x/sys/windows/zsyscall_windows.go b/tools/vendor/golang.org/x/sys/windows/zsyscall_windows.go index 9bb979a3e4..4c2e1bdc01 100644 --- a/tools/vendor/golang.org/x/sys/windows/zsyscall_windows.go +++ b/tools/vendor/golang.org/x/sys/windows/zsyscall_windows.go @@ -247,7 +247,9 @@ var ( procGetCommandLineW = modkernel32.NewProc("GetCommandLineW") procGetComputerNameExW = modkernel32.NewProc("GetComputerNameExW") procGetComputerNameW = modkernel32.NewProc("GetComputerNameW") + procGetConsoleCP = modkernel32.NewProc("GetConsoleCP") procGetConsoleMode = modkernel32.NewProc("GetConsoleMode") + procGetConsoleOutputCP = modkernel32.NewProc("GetConsoleOutputCP") procGetConsoleScreenBufferInfo = modkernel32.NewProc("GetConsoleScreenBufferInfo") procGetCurrentDirectoryW = modkernel32.NewProc("GetCurrentDirectoryW") procGetCurrentProcessId = modkernel32.NewProc("GetCurrentProcessId") @@ -347,8 +349,10 @@ var ( procSetCommMask = modkernel32.NewProc("SetCommMask") procSetCommState = modkernel32.NewProc("SetCommState") procSetCommTimeouts = modkernel32.NewProc("SetCommTimeouts") + procSetConsoleCP = modkernel32.NewProc("SetConsoleCP") procSetConsoleCursorPosition = modkernel32.NewProc("SetConsoleCursorPosition") procSetConsoleMode = modkernel32.NewProc("SetConsoleMode") + procSetConsoleOutputCP = modkernel32.NewProc("SetConsoleOutputCP") procSetCurrentDirectoryW = modkernel32.NewProc("SetCurrentDirectoryW") procSetDefaultDllDirectories = modkernel32.NewProc("SetDefaultDllDirectories") procSetDllDirectoryW = modkernel32.NewProc("SetDllDirectoryW") @@ -2162,6 +2166,15 @@ func GetComputerName(buf *uint16, n *uint32) (err error) { return } +func GetConsoleCP() (cp uint32, err error) { + r0, _, e1 := syscall.Syscall(procGetConsoleCP.Addr(), 0, 0, 0, 0) + cp = uint32(r0) + if cp == 0 { + err = errnoErr(e1) + } + return +} + func GetConsoleMode(console Handle, mode *uint32) (err error) { r1, _, e1 := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(console), uintptr(unsafe.Pointer(mode)), 0) if r1 == 0 { @@ -2170,6 +2183,15 @@ func GetConsoleMode(console Handle, mode *uint32) (err error) { return } +func GetConsoleOutputCP() (cp uint32, err error) { + r0, _, e1 := syscall.Syscall(procGetConsoleOutputCP.Addr(), 0, 0, 0, 0) + cp = uint32(r0) + if cp == 0 { + err = errnoErr(e1) + } + return +} + func GetConsoleScreenBufferInfo(console Handle, info *ConsoleScreenBufferInfo) (err error) { r1, _, e1 := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, uintptr(console), uintptr(unsafe.Pointer(info)), 0) if r1 == 0 { @@ -3038,6 +3060,14 @@ func SetCommTimeouts(handle Handle, timeouts *CommTimeouts) (err error) { return } +func SetConsoleCP(cp uint32) (err error) { + r1, _, e1 := syscall.Syscall(procSetConsoleCP.Addr(), 1, uintptr(cp), 0, 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func setConsoleCursorPosition(console Handle, position uint32) (err error) { r1, _, e1 := syscall.Syscall(procSetConsoleCursorPosition.Addr(), 2, uintptr(console), uintptr(position), 0) if r1 == 0 { @@ -3054,6 +3084,14 @@ func SetConsoleMode(console Handle, mode uint32) (err error) { return } +func SetConsoleOutputCP(cp uint32) (err error) { + r1, _, e1 := syscall.Syscall(procSetConsoleOutputCP.Addr(), 1, uintptr(cp), 0, 0) + if r1 == 0 { + err = errnoErr(e1) + } + return +} + func SetCurrentDirectory(path *uint16) (err error) { r1, _, e1 := syscall.Syscall(procSetCurrentDirectoryW.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0) if r1 == 0 { diff --git a/tools/vendor/golang.org/x/text/LICENSE b/tools/vendor/golang.org/x/text/LICENSE index 6a66aea5ea..2a7cf70da6 100644 --- a/tools/vendor/golang.org/x/text/LICENSE +++ b/tools/vendor/golang.org/x/text/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. +Copyright 2009 The Go Authors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -10,7 +10,7 @@ notice, this list of conditions and the following disclaimer. copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Google Inc. nor the names of its + * Neither the name of Google LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/tools/vendor/google.golang.org/protobuf/encoding/prototext/decode.go b/tools/vendor/google.golang.org/protobuf/encoding/prototext/decode.go index a45f112bce..24bc98ac42 100644 --- a/tools/vendor/google.golang.org/protobuf/encoding/prototext/decode.go +++ b/tools/vendor/google.golang.org/protobuf/encoding/prototext/decode.go @@ -84,7 +84,7 @@ type decoder struct { } // newError returns an error object with position info. -func (d decoder) newError(pos int, f string, x ...interface{}) error { +func (d decoder) newError(pos int, f string, x ...any) error { line, column := d.Position(pos) head := fmt.Sprintf("(line %d:%d): ", line, column) return errors.New(head+f, x...) @@ -96,7 +96,7 @@ func (d decoder) unexpectedTokenError(tok text.Token) error { } // syntaxError returns a syntax error for given position. -func (d decoder) syntaxError(pos int, f string, x ...interface{}) error { +func (d decoder) syntaxError(pos int, f string, x ...any) error { line, column := d.Position(pos) head := fmt.Sprintf("syntax error (line %d:%d): ", line, column) return errors.New(head+f, x...) diff --git a/tools/vendor/google.golang.org/protobuf/encoding/prototext/encode.go b/tools/vendor/google.golang.org/protobuf/encoding/prototext/encode.go index 95967e8112..1f57e6610a 100644 --- a/tools/vendor/google.golang.org/protobuf/encoding/prototext/encode.go +++ b/tools/vendor/google.golang.org/protobuf/encoding/prototext/encode.go @@ -27,15 +27,17 @@ const defaultIndent = " " // Format formats the message as a multiline string. // This function is only intended for human consumption and ignores errors. -// Do not depend on the output being stable. It may change over time across -// different versions of the program. +// Do not depend on the output being stable. Its output will change across +// different builds of your program, even when using the same version of the +// protobuf module. func Format(m proto.Message) string { return MarshalOptions{Multiline: true}.Format(m) } // Marshal writes the given [proto.Message] in textproto format using default -// options. Do not depend on the output being stable. It may change over time -// across different versions of the program. +// options. Do not depend on the output being stable. Its output will change +// across different builds of your program, even when using the same version of +// the protobuf module. func Marshal(m proto.Message) ([]byte, error) { return MarshalOptions{}.Marshal(m) } @@ -84,8 +86,9 @@ type MarshalOptions struct { // Format formats the message as a string. // This method is only intended for human consumption and ignores errors. -// Do not depend on the output being stable. It may change over time across -// different versions of the program. +// Do not depend on the output being stable. Its output will change across +// different builds of your program, even when using the same version of the +// protobuf module. func (o MarshalOptions) Format(m proto.Message) string { if m == nil || !m.ProtoReflect().IsValid() { return "" // invalid syntax, but okay since this is for debugging @@ -98,8 +101,9 @@ func (o MarshalOptions) Format(m proto.Message) string { } // Marshal writes the given [proto.Message] in textproto format using options in -// MarshalOptions object. Do not depend on the output being stable. It may -// change over time across different versions of the program. +// MarshalOptions object. Do not depend on the output being stable. Its output +// will change across different builds of your program, even when using the +// same version of the protobuf module. func (o MarshalOptions) Marshal(m proto.Message) ([]byte, error) { return o.marshal(nil, m) } diff --git a/tools/vendor/google.golang.org/protobuf/internal/descfmt/stringer.go b/tools/vendor/google.golang.org/protobuf/internal/descfmt/stringer.go index a45625c8d1..87e46bd4df 100644 --- a/tools/vendor/google.golang.org/protobuf/internal/descfmt/stringer.go +++ b/tools/vendor/google.golang.org/protobuf/internal/descfmt/stringer.go @@ -252,6 +252,7 @@ func formatDescOpt(t protoreflect.Descriptor, isRoot, allowMulti bool, record fu {rv.MethodByName("Values"), "Values"}, {rv.MethodByName("ReservedNames"), "ReservedNames"}, {rv.MethodByName("ReservedRanges"), "ReservedRanges"}, + {rv.MethodByName("IsClosed"), "IsClosed"}, }...) case protoreflect.EnumValueDescriptor: diff --git a/tools/vendor/google.golang.org/protobuf/internal/editiondefaults/editions_defaults.binpb b/tools/vendor/google.golang.org/protobuf/internal/editiondefaults/editions_defaults.binpb index 18f0756874..ff6a38360a 100644 Binary files a/tools/vendor/google.golang.org/protobuf/internal/editiondefaults/editions_defaults.binpb and b/tools/vendor/google.golang.org/protobuf/internal/editiondefaults/editions_defaults.binpb differ diff --git a/tools/vendor/google.golang.org/protobuf/internal/editionssupport/editions.go b/tools/vendor/google.golang.org/protobuf/internal/editionssupport/editions.go new file mode 100644 index 0000000000..029a6a12d7 --- /dev/null +++ b/tools/vendor/google.golang.org/protobuf/internal/editionssupport/editions.go @@ -0,0 +1,13 @@ +// Copyright 2024 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package editionssupport defines constants for editions that are supported. +package editionssupport + +import descriptorpb "google.golang.org/protobuf/types/descriptorpb" + +const ( + Minimum = descriptorpb.Edition_EDITION_PROTO2 + Maximum = descriptorpb.Edition_EDITION_2023 +) diff --git a/tools/vendor/google.golang.org/protobuf/internal/encoding/tag/tag.go b/tools/vendor/google.golang.org/protobuf/internal/encoding/tag/tag.go index 373d208374..7e87c76044 100644 --- a/tools/vendor/google.golang.org/protobuf/internal/encoding/tag/tag.go +++ b/tools/vendor/google.golang.org/protobuf/internal/encoding/tag/tag.go @@ -32,6 +32,7 @@ var byteType = reflect.TypeOf(byte(0)) func Unmarshal(tag string, goType reflect.Type, evs protoreflect.EnumValueDescriptors) protoreflect.FieldDescriptor { f := new(filedesc.Field) f.L0.ParentFile = filedesc.SurrogateProto2 + f.L1.EditionFeatures = f.L0.ParentFile.L1.EditionFeatures for len(tag) > 0 { i := strings.IndexByte(tag, ',') if i < 0 { @@ -107,8 +108,7 @@ func Unmarshal(tag string, goType reflect.Type, evs protoreflect.EnumValueDescri f.L1.StringName.InitJSON(jsonName) } case s == "packed": - f.L1.HasPacked = true - f.L1.IsPacked = true + f.L1.EditionFeatures.IsPacked = true case strings.HasPrefix(s, "weak="): f.L1.IsWeak = true f.L1.Message = filedesc.PlaceholderMessage(protoreflect.FullName(s[len("weak="):])) diff --git a/tools/vendor/google.golang.org/protobuf/internal/encoding/text/decode.go b/tools/vendor/google.golang.org/protobuf/internal/encoding/text/decode.go index 87853e786d..099b2bf451 100644 --- a/tools/vendor/google.golang.org/protobuf/internal/encoding/text/decode.go +++ b/tools/vendor/google.golang.org/protobuf/internal/encoding/text/decode.go @@ -601,7 +601,7 @@ func (d *Decoder) consumeToken(kind Kind, size int, attrs uint8) Token { // newSyntaxError returns a syntax error with line and column information for // current position. -func (d *Decoder) newSyntaxError(f string, x ...interface{}) error { +func (d *Decoder) newSyntaxError(f string, x ...any) error { e := errors.New(f, x...) line, column := d.Position(len(d.orig) - len(d.in)) return errors.New("syntax error (line %d:%d): %v", line, column, e) diff --git a/tools/vendor/google.golang.org/protobuf/internal/errors/errors.go b/tools/vendor/google.golang.org/protobuf/internal/errors/errors.go index 20c17b35e3..c2d6bd5265 100644 --- a/tools/vendor/google.golang.org/protobuf/internal/errors/errors.go +++ b/tools/vendor/google.golang.org/protobuf/internal/errors/errors.go @@ -17,7 +17,7 @@ var Error = errors.New("protobuf error") // New formats a string according to the format specifier and arguments and // returns an error that has a "proto" prefix. -func New(f string, x ...interface{}) error { +func New(f string, x ...any) error { return &prefixError{s: format(f, x...)} } @@ -43,7 +43,7 @@ func (e *prefixError) Unwrap() error { // Wrap returns an error that has a "proto" prefix, the formatted string described // by the format specifier and arguments, and a suffix of err. The error wraps err. -func Wrap(err error, f string, x ...interface{}) error { +func Wrap(err error, f string, x ...any) error { return &wrapError{ s: format(f, x...), err: err, @@ -67,7 +67,7 @@ func (e *wrapError) Is(target error) bool { return target == Error } -func format(f string, x ...interface{}) string { +func format(f string, x ...any) string { // avoid "proto: " prefix when chaining for i := 0; i < len(x); i++ { switch e := x[i].(type) { @@ -87,3 +87,18 @@ func InvalidUTF8(name string) error { func RequiredNotSet(name string) error { return New("required field %v not set", name) } + +type SizeMismatchError struct { + Calculated, Measured int +} + +func (e *SizeMismatchError) Error() string { + return fmt.Sprintf("size mismatch (see https://github.com/golang/protobuf/issues/1609): calculated=%d, measured=%d", e.Calculated, e.Measured) +} + +func MismatchedSizeCalculation(calculated, measured int) error { + return &SizeMismatchError{ + Calculated: calculated, + Measured: measured, + } +} diff --git a/tools/vendor/google.golang.org/protobuf/internal/filedesc/desc.go b/tools/vendor/google.golang.org/protobuf/internal/filedesc/desc.go index 8826bcf402..df53ff40b2 100644 --- a/tools/vendor/google.golang.org/protobuf/internal/filedesc/desc.go +++ b/tools/vendor/google.golang.org/protobuf/internal/filedesc/desc.go @@ -7,6 +7,7 @@ package filedesc import ( "bytes" "fmt" + "strings" "sync" "sync/atomic" @@ -108,9 +109,12 @@ func (fd *File) ParentFile() protoreflect.FileDescriptor { return fd } func (fd *File) Parent() protoreflect.Descriptor { return nil } func (fd *File) Index() int { return 0 } func (fd *File) Syntax() protoreflect.Syntax { return fd.L1.Syntax } -func (fd *File) Name() protoreflect.Name { return fd.L1.Package.Name() } -func (fd *File) FullName() protoreflect.FullName { return fd.L1.Package } -func (fd *File) IsPlaceholder() bool { return false } + +// Not exported and just used to reconstruct the original FileDescriptor proto +func (fd *File) Edition() int32 { return int32(fd.L1.Edition) } +func (fd *File) Name() protoreflect.Name { return fd.L1.Package.Name() } +func (fd *File) FullName() protoreflect.FullName { return fd.L1.Package } +func (fd *File) IsPlaceholder() bool { return false } func (fd *File) Options() protoreflect.ProtoMessage { if f := fd.lazyInit().Options; f != nil { return f() @@ -202,6 +206,9 @@ func (ed *Enum) lazyInit() *EnumL2 { ed.L0.ParentFile.lazyInit() // implicitly initializes L2 return ed.L2 } +func (ed *Enum) IsClosed() bool { + return !ed.L1.EditionFeatures.IsOpenEnum +} func (ed *EnumValue) Options() protoreflect.ProtoMessage { if f := ed.L1.Options; f != nil { @@ -251,10 +258,6 @@ type ( StringName stringName IsProto3Optional bool // promoted from google.protobuf.FieldDescriptorProto IsWeak bool // promoted from google.protobuf.FieldOptions - HasPacked bool // promoted from google.protobuf.FieldOptions - IsPacked bool // promoted from google.protobuf.FieldOptions - HasEnforceUTF8 bool // promoted from google.protobuf.FieldOptions - EnforceUTF8 bool // promoted from google.protobuf.FieldOptions Default defaultValue ContainingOneof protoreflect.OneofDescriptor // must be consistent with Message.Oneofs.Fields Enum protoreflect.EnumDescriptor @@ -331,8 +334,7 @@ func (fd *Field) HasPresence() bool { if fd.L1.Cardinality == protoreflect.Repeated { return false } - explicitFieldPresence := fd.Syntax() == protoreflect.Editions && fd.L1.EditionFeatures.IsFieldPresence - return fd.Syntax() == protoreflect.Proto2 || explicitFieldPresence || fd.L1.Message != nil || fd.L1.ContainingOneof != nil + return fd.IsExtension() || fd.L1.EditionFeatures.IsFieldPresence || fd.L1.Message != nil || fd.L1.ContainingOneof != nil } func (fd *Field) HasOptionalKeyword() bool { return (fd.L0.ParentFile.L1.Syntax == protoreflect.Proto2 && fd.L1.Cardinality == protoreflect.Optional && fd.L1.ContainingOneof == nil) || fd.L1.IsProto3Optional @@ -345,14 +347,7 @@ func (fd *Field) IsPacked() bool { case protoreflect.StringKind, protoreflect.BytesKind, protoreflect.MessageKind, protoreflect.GroupKind: return false } - if fd.L0.ParentFile.L1.Syntax == protoreflect.Editions { - return fd.L1.EditionFeatures.IsPacked - } - if fd.L0.ParentFile.L1.Syntax == protoreflect.Proto3 { - // proto3 repeated fields are packed by default. - return !fd.L1.HasPacked || fd.L1.IsPacked - } - return fd.L1.IsPacked + return fd.L1.EditionFeatures.IsPacked } func (fd *Field) IsExtension() bool { return false } func (fd *Field) IsWeak() bool { return fd.L1.IsWeak } @@ -388,6 +383,10 @@ func (fd *Field) Message() protoreflect.MessageDescriptor { } return fd.L1.Message } +func (fd *Field) IsMapEntry() bool { + parent, ok := fd.L0.Parent.(protoreflect.MessageDescriptor) + return ok && parent.IsMapEntry() +} func (fd *Field) Format(s fmt.State, r rune) { descfmt.FormatDesc(s, r, fd) } func (fd *Field) ProtoType(protoreflect.FieldDescriptor) {} @@ -399,13 +398,7 @@ func (fd *Field) ProtoType(protoreflect.FieldDescriptor) {} // WARNING: This method is exempt from the compatibility promise and may be // removed in the future without warning. func (fd *Field) EnforceUTF8() bool { - if fd.L0.ParentFile.L1.Syntax == protoreflect.Editions { - return fd.L1.EditionFeatures.IsUTF8Validated - } - if fd.L1.HasEnforceUTF8 { - return fd.L1.EnforceUTF8 - } - return fd.L0.ParentFile.L1.Syntax == protoreflect.Proto3 + return fd.L1.EditionFeatures.IsUTF8Validated } func (od *Oneof) IsSynthetic() bool { @@ -438,7 +431,6 @@ type ( Options func() protoreflect.ProtoMessage StringName stringName IsProto3Optional bool // promoted from google.protobuf.FieldDescriptorProto - IsPacked bool // promoted from google.protobuf.FieldOptions Default defaultValue Enum protoreflect.EnumDescriptor Message protoreflect.MessageDescriptor @@ -461,7 +453,16 @@ func (xd *Extension) HasPresence() bool { return xd.L1.Cardi func (xd *Extension) HasOptionalKeyword() bool { return (xd.L0.ParentFile.L1.Syntax == protoreflect.Proto2 && xd.L1.Cardinality == protoreflect.Optional) || xd.lazyInit().IsProto3Optional } -func (xd *Extension) IsPacked() bool { return xd.lazyInit().IsPacked } +func (xd *Extension) IsPacked() bool { + if xd.L1.Cardinality != protoreflect.Repeated { + return false + } + switch xd.L1.Kind { + case protoreflect.StringKind, protoreflect.BytesKind, protoreflect.MessageKind, protoreflect.GroupKind: + return false + } + return xd.L1.EditionFeatures.IsPacked +} func (xd *Extension) IsExtension() bool { return true } func (xd *Extension) IsWeak() bool { return false } func (xd *Extension) IsList() bool { return xd.Cardinality() == protoreflect.Repeated } @@ -542,8 +543,9 @@ func (md *Method) ProtoInternal(pragma.DoNotImplement) {} // Surrogate files are can be used to create standalone descriptors // where the syntax is only information derived from the parent file. var ( - SurrogateProto2 = &File{L1: FileL1{Syntax: protoreflect.Proto2}, L2: &FileL2{}} - SurrogateProto3 = &File{L1: FileL1{Syntax: protoreflect.Proto3}, L2: &FileL2{}} + SurrogateProto2 = &File{L1: FileL1{Syntax: protoreflect.Proto2}, L2: &FileL2{}} + SurrogateProto3 = &File{L1: FileL1{Syntax: protoreflect.Proto3}, L2: &FileL2{}} + SurrogateEdition2023 = &File{L1: FileL1{Syntax: protoreflect.Editions, Edition: Edition2023}, L2: &FileL2{}} ) type ( @@ -585,6 +587,34 @@ func (s *stringName) InitJSON(name string) { s.nameJSON = name } +// Returns true if this field is structured like the synthetic field of a proto2 +// group. This allows us to expand our treatment of delimited fields without +// breaking proto2 files that have been upgraded to editions. +func isGroupLike(fd protoreflect.FieldDescriptor) bool { + // Groups are always group types. + if fd.Kind() != protoreflect.GroupKind { + return false + } + + // Group fields are always the lowercase type name. + if strings.ToLower(string(fd.Message().Name())) != string(fd.Name()) { + return false + } + + // Groups could only be defined in the same file they're used. + if fd.Message().ParentFile() != fd.ParentFile() { + return false + } + + // Group messages are always defined in the same scope as the field. File + // level extensions will compare NULL == NULL here, which is why the file + // comparison above is necessary to ensure both come from the same file. + if fd.IsExtension() { + return fd.Parent() == fd.Message().Parent() + } + return fd.ContainingMessage() == fd.Message().Parent() +} + func (s *stringName) lazyInit(fd protoreflect.FieldDescriptor) *stringName { s.once.Do(func() { if fd.IsExtension() { @@ -605,7 +635,7 @@ func (s *stringName) lazyInit(fd protoreflect.FieldDescriptor) *stringName { // Format the text name. s.nameText = string(fd.Name()) - if fd.Kind() == protoreflect.GroupKind { + if isGroupLike(fd) { s.nameText = string(fd.Message().Name()) } } diff --git a/tools/vendor/google.golang.org/protobuf/internal/filedesc/desc_init.go b/tools/vendor/google.golang.org/protobuf/internal/filedesc/desc_init.go index 237e64fd23..8a57d60b08 100644 --- a/tools/vendor/google.golang.org/protobuf/internal/filedesc/desc_init.go +++ b/tools/vendor/google.golang.org/protobuf/internal/filedesc/desc_init.go @@ -113,8 +113,10 @@ func (fd *File) unmarshalSeed(b []byte) { switch string(v) { case "proto2": fd.L1.Syntax = protoreflect.Proto2 + fd.L1.Edition = EditionProto2 case "proto3": fd.L1.Syntax = protoreflect.Proto3 + fd.L1.Edition = EditionProto3 case "editions": fd.L1.Syntax = protoreflect.Editions default: @@ -177,11 +179,10 @@ func (fd *File) unmarshalSeed(b []byte) { // If syntax is missing, it is assumed to be proto2. if fd.L1.Syntax == 0 { fd.L1.Syntax = protoreflect.Proto2 + fd.L1.Edition = EditionProto2 } - if fd.L1.Syntax == protoreflect.Editions { - fd.L1.EditionFeatures = getFeaturesFor(fd.L1.Edition) - } + fd.L1.EditionFeatures = getFeaturesFor(fd.L1.Edition) // Parse editions features from options if any if options != nil { @@ -267,6 +268,7 @@ func (ed *Enum) unmarshalSeed(b []byte, sb *strs.Builder, pf *File, pd protorefl ed.L0.ParentFile = pf ed.L0.Parent = pd ed.L0.Index = i + ed.L1.EditionFeatures = featuresFromParentDesc(ed.Parent()) var numValues int for b := b; len(b) > 0; { @@ -443,6 +445,7 @@ func (xd *Extension) unmarshalSeed(b []byte, sb *strs.Builder, pf *File, pd prot xd.L0.ParentFile = pf xd.L0.Parent = pd xd.L0.Index = i + xd.L1.EditionFeatures = featuresFromParentDesc(pd) for len(b) > 0 { num, typ, n := protowire.ConsumeTag(b) @@ -467,6 +470,38 @@ func (xd *Extension) unmarshalSeed(b []byte, sb *strs.Builder, pf *File, pd prot xd.L0.FullName = appendFullName(sb, pd.FullName(), v) case genid.FieldDescriptorProto_Extendee_field_number: xd.L1.Extendee = PlaceholderMessage(makeFullName(sb, v)) + case genid.FieldDescriptorProto_Options_field_number: + xd.unmarshalOptions(v) + } + default: + m := protowire.ConsumeFieldValue(num, typ, b) + b = b[m:] + } + } + + if xd.L1.Kind == protoreflect.MessageKind && xd.L1.EditionFeatures.IsDelimitedEncoded { + xd.L1.Kind = protoreflect.GroupKind + } +} + +func (xd *Extension) unmarshalOptions(b []byte) { + for len(b) > 0 { + num, typ, n := protowire.ConsumeTag(b) + b = b[n:] + switch typ { + case protowire.VarintType: + v, m := protowire.ConsumeVarint(b) + b = b[m:] + switch num { + case genid.FieldOptions_Packed_field_number: + xd.L1.EditionFeatures.IsPacked = protowire.DecodeBool(v) + } + case protowire.BytesType: + v, m := protowire.ConsumeBytes(b) + b = b[m:] + switch num { + case genid.FieldOptions_Features_field_number: + xd.L1.EditionFeatures = unmarshalFeatureSet(v, xd.L1.EditionFeatures) } default: m := protowire.ConsumeFieldValue(num, typ, b) @@ -499,7 +534,7 @@ func (sd *Service) unmarshalSeed(b []byte, sb *strs.Builder, pf *File, pd protor } var nameBuilderPool = sync.Pool{ - New: func() interface{} { return new(strs.Builder) }, + New: func() any { return new(strs.Builder) }, } func getBuilder() *strs.Builder { diff --git a/tools/vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go b/tools/vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go index 482a61cc10..e56c91a8db 100644 --- a/tools/vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go +++ b/tools/vendor/google.golang.org/protobuf/internal/filedesc/desc_lazy.go @@ -45,6 +45,11 @@ func (file *File) resolveMessages() { case protoreflect.MessageKind, protoreflect.GroupKind: fd.L1.Message = file.resolveMessageDependency(fd.L1.Message, listFieldDeps, depIdx) depIdx++ + if fd.L1.Kind == protoreflect.GroupKind && (fd.IsMap() || fd.IsMapEntry()) { + // A map field might inherit delimited encoding from a file-wide default feature. + // But maps never actually use delimited encoding. (At least for now...) + fd.L1.Kind = protoreflect.MessageKind + } } // Default is resolved here since it depends on Enum being resolved. @@ -466,10 +471,10 @@ func (fd *Field) unmarshalFull(b []byte, sb *strs.Builder, pf *File, pd protoref b = b[m:] } } - if fd.Syntax() == protoreflect.Editions && fd.L1.Kind == protoreflect.MessageKind && fd.L1.EditionFeatures.IsDelimitedEncoded { + if fd.L1.Kind == protoreflect.MessageKind && fd.L1.EditionFeatures.IsDelimitedEncoded { fd.L1.Kind = protoreflect.GroupKind } - if fd.Syntax() == protoreflect.Editions && fd.L1.EditionFeatures.IsLegacyRequired { + if fd.L1.EditionFeatures.IsLegacyRequired { fd.L1.Cardinality = protoreflect.Required } if rawTypeName != nil { @@ -496,13 +501,11 @@ func (fd *Field) unmarshalOptions(b []byte) { b = b[m:] switch num { case genid.FieldOptions_Packed_field_number: - fd.L1.HasPacked = true - fd.L1.IsPacked = protowire.DecodeBool(v) + fd.L1.EditionFeatures.IsPacked = protowire.DecodeBool(v) case genid.FieldOptions_Weak_field_number: fd.L1.IsWeak = protowire.DecodeBool(v) case FieldOptions_EnforceUTF8: - fd.L1.HasEnforceUTF8 = true - fd.L1.EnforceUTF8 = protowire.DecodeBool(v) + fd.L1.EditionFeatures.IsUTF8Validated = protowire.DecodeBool(v) } case protowire.BytesType: v, m := protowire.ConsumeBytes(b) @@ -548,7 +551,6 @@ func (od *Oneof) unmarshalFull(b []byte, sb *strs.Builder, pf *File, pd protoref func (xd *Extension) unmarshalFull(b []byte, sb *strs.Builder) { var rawTypeName []byte var rawOptions []byte - xd.L1.EditionFeatures = featuresFromParentDesc(xd.L1.Extendee) xd.L2 = new(ExtensionL2) for len(b) > 0 { num, typ, n := protowire.ConsumeTag(b) @@ -572,7 +574,6 @@ func (xd *Extension) unmarshalFull(b []byte, sb *strs.Builder) { case genid.FieldDescriptorProto_TypeName_field_number: rawTypeName = v case genid.FieldDescriptorProto_Options_field_number: - xd.unmarshalOptions(v) rawOptions = appendOptions(rawOptions, v) } default: @@ -580,12 +581,6 @@ func (xd *Extension) unmarshalFull(b []byte, sb *strs.Builder) { b = b[m:] } } - if xd.Syntax() == protoreflect.Editions && xd.L1.Kind == protoreflect.MessageKind && xd.L1.EditionFeatures.IsDelimitedEncoded { - xd.L1.Kind = protoreflect.GroupKind - } - if xd.Syntax() == protoreflect.Editions && xd.L1.EditionFeatures.IsLegacyRequired { - xd.L1.Cardinality = protoreflect.Required - } if rawTypeName != nil { name := makeFullName(sb, rawTypeName) switch xd.L1.Kind { @@ -598,32 +593,6 @@ func (xd *Extension) unmarshalFull(b []byte, sb *strs.Builder) { xd.L2.Options = xd.L0.ParentFile.builder.optionsUnmarshaler(&descopts.Field, rawOptions) } -func (xd *Extension) unmarshalOptions(b []byte) { - for len(b) > 0 { - num, typ, n := protowire.ConsumeTag(b) - b = b[n:] - switch typ { - case protowire.VarintType: - v, m := protowire.ConsumeVarint(b) - b = b[m:] - switch num { - case genid.FieldOptions_Packed_field_number: - xd.L2.IsPacked = protowire.DecodeBool(v) - } - case protowire.BytesType: - v, m := protowire.ConsumeBytes(b) - b = b[m:] - switch num { - case genid.FieldOptions_Features_field_number: - xd.L1.EditionFeatures = unmarshalFeatureSet(v, xd.L1.EditionFeatures) - } - default: - m := protowire.ConsumeFieldValue(num, typ, b) - b = b[m:] - } - } -} - func (sd *Service) unmarshalFull(b []byte, sb *strs.Builder) { var rawMethods [][]byte var rawOptions []byte diff --git a/tools/vendor/google.golang.org/protobuf/internal/filedesc/desc_list_gen.go b/tools/vendor/google.golang.org/protobuf/internal/filedesc/desc_list_gen.go index 30db19fdc7..f4107c05f4 100644 --- a/tools/vendor/google.golang.org/protobuf/internal/filedesc/desc_list_gen.go +++ b/tools/vendor/google.golang.org/protobuf/internal/filedesc/desc_list_gen.go @@ -8,6 +8,7 @@ package filedesc import ( "fmt" + "strings" "sync" "google.golang.org/protobuf/internal/descfmt" @@ -198,6 +199,16 @@ func (p *Fields) lazyInit() *Fields { if _, ok := p.byText[d.TextName()]; !ok { p.byText[d.TextName()] = d } + if isGroupLike(d) { + lowerJSONName := strings.ToLower(d.JSONName()) + if _, ok := p.byJSON[lowerJSONName]; !ok { + p.byJSON[lowerJSONName] = d + } + lowerTextName := strings.ToLower(d.TextName()) + if _, ok := p.byText[lowerTextName]; !ok { + p.byText[lowerTextName] = d + } + } if _, ok := p.byNum[d.Number()]; !ok { p.byNum[d.Number()] = d } diff --git a/tools/vendor/google.golang.org/protobuf/internal/filedesc/editions.go b/tools/vendor/google.golang.org/protobuf/internal/filedesc/editions.go index 0375a49d40..11f5f356b6 100644 --- a/tools/vendor/google.golang.org/protobuf/internal/filedesc/editions.go +++ b/tools/vendor/google.golang.org/protobuf/internal/filedesc/editions.go @@ -14,9 +14,13 @@ import ( ) var defaultsCache = make(map[Edition]EditionFeatures) +var defaultsKeys = []Edition{} func init() { unmarshalEditionDefaults(editiondefaults.Defaults) + SurrogateProto2.L1.EditionFeatures = getFeaturesFor(EditionProto2) + SurrogateProto3.L1.EditionFeatures = getFeaturesFor(EditionProto3) + SurrogateEdition2023.L1.EditionFeatures = getFeaturesFor(Edition2023) } func unmarshalGoFeature(b []byte, parent EditionFeatures) EditionFeatures { @@ -104,12 +108,15 @@ func unmarshalEditionDefault(b []byte) { v, m := protowire.ConsumeBytes(b) b = b[m:] switch num { - case genid.FeatureSetDefaults_FeatureSetEditionDefault_Features_field_number: + case genid.FeatureSetDefaults_FeatureSetEditionDefault_FixedFeatures_field_number: + fs = unmarshalFeatureSet(v, fs) + case genid.FeatureSetDefaults_FeatureSetEditionDefault_OverridableFeatures_field_number: fs = unmarshalFeatureSet(v, fs) } } } defaultsCache[ed] = fs + defaultsKeys = append(defaultsKeys, ed) } func unmarshalEditionDefaults(b []byte) { @@ -135,8 +142,15 @@ func unmarshalEditionDefaults(b []byte) { } func getFeaturesFor(ed Edition) EditionFeatures { - if def, ok := defaultsCache[ed]; ok { - return def + match := EditionUnknown + for _, key := range defaultsKeys { + if key > ed { + break + } + match = key + } + if match == EditionUnknown { + panic(fmt.Sprintf("unsupported edition: %v", ed)) } - panic(fmt.Sprintf("unsupported edition: %v", ed)) + return defaultsCache[match] } diff --git a/tools/vendor/google.golang.org/protobuf/internal/filedesc/placeholder.go b/tools/vendor/google.golang.org/protobuf/internal/filedesc/placeholder.go index 28240ebc5c..bfb3b84170 100644 --- a/tools/vendor/google.golang.org/protobuf/internal/filedesc/placeholder.go +++ b/tools/vendor/google.golang.org/protobuf/internal/filedesc/placeholder.go @@ -63,6 +63,7 @@ func (e PlaceholderEnum) Options() protoreflect.ProtoMessage { return des func (e PlaceholderEnum) Values() protoreflect.EnumValueDescriptors { return emptyEnumValues } func (e PlaceholderEnum) ReservedNames() protoreflect.Names { return emptyNames } func (e PlaceholderEnum) ReservedRanges() protoreflect.EnumRanges { return emptyEnumRanges } +func (e PlaceholderEnum) IsClosed() bool { return false } func (e PlaceholderEnum) ProtoType(protoreflect.EnumDescriptor) { return } func (e PlaceholderEnum) ProtoInternal(pragma.DoNotImplement) { return } diff --git a/tools/vendor/google.golang.org/protobuf/internal/filetype/build.go b/tools/vendor/google.golang.org/protobuf/internal/filetype/build.go index f0e38c4ef4..ba83fea44c 100644 --- a/tools/vendor/google.golang.org/protobuf/internal/filetype/build.go +++ b/tools/vendor/google.golang.org/protobuf/internal/filetype/build.go @@ -68,7 +68,7 @@ type Builder struct { // and for input and output messages referenced by service methods. // Dependencies must come after declarations, but the ordering of // dependencies themselves is unspecified. - GoTypes []interface{} + GoTypes []any // DependencyIndexes is an ordered list of indexes into GoTypes for the // dependencies of messages, extensions, or services. @@ -268,7 +268,7 @@ func (x depIdxs) Get(i, j int32) int32 { type ( resolverByIndex struct { - goTypes []interface{} + goTypes []any depIdxs depIdxs fileRegistry } diff --git a/tools/vendor/google.golang.org/protobuf/internal/genid/descriptor_gen.go b/tools/vendor/google.golang.org/protobuf/internal/genid/descriptor_gen.go index 40272c893f..f30ab6b586 100644 --- a/tools/vendor/google.golang.org/protobuf/internal/genid/descriptor_gen.go +++ b/tools/vendor/google.golang.org/protobuf/internal/genid/descriptor_gen.go @@ -21,6 +21,7 @@ const ( // Enum values for google.protobuf.Edition. const ( Edition_EDITION_UNKNOWN_enum_value = 0 + Edition_EDITION_LEGACY_enum_value = 900 Edition_EDITION_PROTO2_enum_value = 998 Edition_EDITION_PROTO3_enum_value = 999 Edition_EDITION_2023_enum_value = 1000 @@ -653,6 +654,7 @@ const ( FieldOptions_Targets_field_name protoreflect.Name = "targets" FieldOptions_EditionDefaults_field_name protoreflect.Name = "edition_defaults" FieldOptions_Features_field_name protoreflect.Name = "features" + FieldOptions_FeatureSupport_field_name protoreflect.Name = "feature_support" FieldOptions_UninterpretedOption_field_name protoreflect.Name = "uninterpreted_option" FieldOptions_Ctype_field_fullname protoreflect.FullName = "google.protobuf.FieldOptions.ctype" @@ -667,6 +669,7 @@ const ( FieldOptions_Targets_field_fullname protoreflect.FullName = "google.protobuf.FieldOptions.targets" FieldOptions_EditionDefaults_field_fullname protoreflect.FullName = "google.protobuf.FieldOptions.edition_defaults" FieldOptions_Features_field_fullname protoreflect.FullName = "google.protobuf.FieldOptions.features" + FieldOptions_FeatureSupport_field_fullname protoreflect.FullName = "google.protobuf.FieldOptions.feature_support" FieldOptions_UninterpretedOption_field_fullname protoreflect.FullName = "google.protobuf.FieldOptions.uninterpreted_option" ) @@ -684,6 +687,7 @@ const ( FieldOptions_Targets_field_number protoreflect.FieldNumber = 19 FieldOptions_EditionDefaults_field_number protoreflect.FieldNumber = 20 FieldOptions_Features_field_number protoreflect.FieldNumber = 21 + FieldOptions_FeatureSupport_field_number protoreflect.FieldNumber = 22 FieldOptions_UninterpretedOption_field_number protoreflect.FieldNumber = 999 ) @@ -767,6 +771,33 @@ const ( FieldOptions_EditionDefault_Value_field_number protoreflect.FieldNumber = 2 ) +// Names for google.protobuf.FieldOptions.FeatureSupport. +const ( + FieldOptions_FeatureSupport_message_name protoreflect.Name = "FeatureSupport" + FieldOptions_FeatureSupport_message_fullname protoreflect.FullName = "google.protobuf.FieldOptions.FeatureSupport" +) + +// Field names for google.protobuf.FieldOptions.FeatureSupport. +const ( + FieldOptions_FeatureSupport_EditionIntroduced_field_name protoreflect.Name = "edition_introduced" + FieldOptions_FeatureSupport_EditionDeprecated_field_name protoreflect.Name = "edition_deprecated" + FieldOptions_FeatureSupport_DeprecationWarning_field_name protoreflect.Name = "deprecation_warning" + FieldOptions_FeatureSupport_EditionRemoved_field_name protoreflect.Name = "edition_removed" + + FieldOptions_FeatureSupport_EditionIntroduced_field_fullname protoreflect.FullName = "google.protobuf.FieldOptions.FeatureSupport.edition_introduced" + FieldOptions_FeatureSupport_EditionDeprecated_field_fullname protoreflect.FullName = "google.protobuf.FieldOptions.FeatureSupport.edition_deprecated" + FieldOptions_FeatureSupport_DeprecationWarning_field_fullname protoreflect.FullName = "google.protobuf.FieldOptions.FeatureSupport.deprecation_warning" + FieldOptions_FeatureSupport_EditionRemoved_field_fullname protoreflect.FullName = "google.protobuf.FieldOptions.FeatureSupport.edition_removed" +) + +// Field numbers for google.protobuf.FieldOptions.FeatureSupport. +const ( + FieldOptions_FeatureSupport_EditionIntroduced_field_number protoreflect.FieldNumber = 1 + FieldOptions_FeatureSupport_EditionDeprecated_field_number protoreflect.FieldNumber = 2 + FieldOptions_FeatureSupport_DeprecationWarning_field_number protoreflect.FieldNumber = 3 + FieldOptions_FeatureSupport_EditionRemoved_field_number protoreflect.FieldNumber = 4 +) + // Names for google.protobuf.OneofOptions. const ( OneofOptions_message_name protoreflect.Name = "OneofOptions" @@ -829,11 +860,13 @@ const ( EnumValueOptions_Deprecated_field_name protoreflect.Name = "deprecated" EnumValueOptions_Features_field_name protoreflect.Name = "features" EnumValueOptions_DebugRedact_field_name protoreflect.Name = "debug_redact" + EnumValueOptions_FeatureSupport_field_name protoreflect.Name = "feature_support" EnumValueOptions_UninterpretedOption_field_name protoreflect.Name = "uninterpreted_option" EnumValueOptions_Deprecated_field_fullname protoreflect.FullName = "google.protobuf.EnumValueOptions.deprecated" EnumValueOptions_Features_field_fullname protoreflect.FullName = "google.protobuf.EnumValueOptions.features" EnumValueOptions_DebugRedact_field_fullname protoreflect.FullName = "google.protobuf.EnumValueOptions.debug_redact" + EnumValueOptions_FeatureSupport_field_fullname protoreflect.FullName = "google.protobuf.EnumValueOptions.feature_support" EnumValueOptions_UninterpretedOption_field_fullname protoreflect.FullName = "google.protobuf.EnumValueOptions.uninterpreted_option" ) @@ -842,6 +875,7 @@ const ( EnumValueOptions_Deprecated_field_number protoreflect.FieldNumber = 1 EnumValueOptions_Features_field_number protoreflect.FieldNumber = 2 EnumValueOptions_DebugRedact_field_number protoreflect.FieldNumber = 3 + EnumValueOptions_FeatureSupport_field_number protoreflect.FieldNumber = 4 EnumValueOptions_UninterpretedOption_field_number protoreflect.FieldNumber = 999 ) @@ -1110,17 +1144,20 @@ const ( // Field names for google.protobuf.FeatureSetDefaults.FeatureSetEditionDefault. const ( - FeatureSetDefaults_FeatureSetEditionDefault_Edition_field_name protoreflect.Name = "edition" - FeatureSetDefaults_FeatureSetEditionDefault_Features_field_name protoreflect.Name = "features" + FeatureSetDefaults_FeatureSetEditionDefault_Edition_field_name protoreflect.Name = "edition" + FeatureSetDefaults_FeatureSetEditionDefault_OverridableFeatures_field_name protoreflect.Name = "overridable_features" + FeatureSetDefaults_FeatureSetEditionDefault_FixedFeatures_field_name protoreflect.Name = "fixed_features" - FeatureSetDefaults_FeatureSetEditionDefault_Edition_field_fullname protoreflect.FullName = "google.protobuf.FeatureSetDefaults.FeatureSetEditionDefault.edition" - FeatureSetDefaults_FeatureSetEditionDefault_Features_field_fullname protoreflect.FullName = "google.protobuf.FeatureSetDefaults.FeatureSetEditionDefault.features" + FeatureSetDefaults_FeatureSetEditionDefault_Edition_field_fullname protoreflect.FullName = "google.protobuf.FeatureSetDefaults.FeatureSetEditionDefault.edition" + FeatureSetDefaults_FeatureSetEditionDefault_OverridableFeatures_field_fullname protoreflect.FullName = "google.protobuf.FeatureSetDefaults.FeatureSetEditionDefault.overridable_features" + FeatureSetDefaults_FeatureSetEditionDefault_FixedFeatures_field_fullname protoreflect.FullName = "google.protobuf.FeatureSetDefaults.FeatureSetEditionDefault.fixed_features" ) // Field numbers for google.protobuf.FeatureSetDefaults.FeatureSetEditionDefault. const ( - FeatureSetDefaults_FeatureSetEditionDefault_Edition_field_number protoreflect.FieldNumber = 3 - FeatureSetDefaults_FeatureSetEditionDefault_Features_field_number protoreflect.FieldNumber = 2 + FeatureSetDefaults_FeatureSetEditionDefault_Edition_field_number protoreflect.FieldNumber = 3 + FeatureSetDefaults_FeatureSetEditionDefault_OverridableFeatures_field_number protoreflect.FieldNumber = 4 + FeatureSetDefaults_FeatureSetEditionDefault_FixedFeatures_field_number protoreflect.FieldNumber = 5 ) // Names for google.protobuf.SourceCodeInfo. diff --git a/tools/vendor/google.golang.org/protobuf/internal/genid/go_features_gen.go b/tools/vendor/google.golang.org/protobuf/internal/genid/go_features_gen.go index fd9015e8ee..9a652a2b42 100644 --- a/tools/vendor/google.golang.org/protobuf/internal/genid/go_features_gen.go +++ b/tools/vendor/google.golang.org/protobuf/internal/genid/go_features_gen.go @@ -10,7 +10,7 @@ import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" ) -const File_reflect_protodesc_proto_go_features_proto = "reflect/protodesc/proto/go_features.proto" +const File_google_protobuf_go_features_proto = "google/protobuf/go_features.proto" // Names for google.protobuf.GoFeatures. const ( diff --git a/tools/vendor/google.golang.org/protobuf/internal/impl/api_export.go b/tools/vendor/google.golang.org/protobuf/internal/impl/api_export.go index a371f98de1..5d5771c2ed 100644 --- a/tools/vendor/google.golang.org/protobuf/internal/impl/api_export.go +++ b/tools/vendor/google.golang.org/protobuf/internal/impl/api_export.go @@ -22,13 +22,13 @@ type Export struct{} // NewError formats a string according to the format specifier and arguments and // returns an error that has a "proto" prefix. -func (Export) NewError(f string, x ...interface{}) error { +func (Export) NewError(f string, x ...any) error { return errors.New(f, x...) } // enum is any enum type generated by protoc-gen-go // and must be a named int32 type. -type enum = interface{} +type enum = any // EnumOf returns the protoreflect.Enum interface over e. // It returns nil if e is nil. @@ -81,7 +81,7 @@ func (Export) EnumStringOf(ed protoreflect.EnumDescriptor, n protoreflect.EnumNu // message is any message type generated by protoc-gen-go // and must be a pointer to a named struct type. -type message = interface{} +type message = any // legacyMessageWrapper wraps a v2 message as a v1 message. type legacyMessageWrapper struct{ m protoreflect.ProtoMessage } diff --git a/tools/vendor/google.golang.org/protobuf/internal/impl/checkinit.go b/tools/vendor/google.golang.org/protobuf/internal/impl/checkinit.go index bff041edc9..f29e6a8fa8 100644 --- a/tools/vendor/google.golang.org/protobuf/internal/impl/checkinit.go +++ b/tools/vendor/google.golang.org/protobuf/internal/impl/checkinit.go @@ -68,7 +68,7 @@ func (mi *MessageInfo) isInitExtensions(ext *map[int32]ExtensionField) error { } for _, x := range *ext { ei := getExtensionFieldInfo(x.Type()) - if ei.funcs.isInit == nil { + if ei.funcs.isInit == nil || x.isUnexpandedLazy() { continue } v := x.Value() diff --git a/tools/vendor/google.golang.org/protobuf/internal/impl/codec_extension.go b/tools/vendor/google.golang.org/protobuf/internal/impl/codec_extension.go index 2b8f122c27..4bb0a7a20c 100644 --- a/tools/vendor/google.golang.org/protobuf/internal/impl/codec_extension.go +++ b/tools/vendor/google.golang.org/protobuf/internal/impl/codec_extension.go @@ -99,6 +99,28 @@ func (f *ExtensionField) canLazy(xt protoreflect.ExtensionType) bool { return false } +// isUnexpandedLazy returns true if the ExensionField is lazy and not +// yet expanded, which means it's present and already checked for +// initialized required fields. +func (f *ExtensionField) isUnexpandedLazy() bool { + return f.lazy != nil && atomic.LoadUint32(&f.lazy.atomicOnce) == 0 +} + +// lazyBuffer retrieves the buffer for a lazy extension if it's not yet expanded. +// +// The returned buffer has to be kept over whatever operation we're planning, +// as re-retrieving it will fail after the message is lazily decoded. +func (f *ExtensionField) lazyBuffer() []byte { + // This function might be in the critical path, so check the atomic without + // taking a look first, then only take the lock if needed. + if !f.isUnexpandedLazy() { + return nil + } + f.lazy.mu.Lock() + defer f.lazy.mu.Unlock() + return f.lazy.b +} + func (f *ExtensionField) lazyInit() { f.lazy.mu.Lock() defer f.lazy.mu.Unlock() diff --git a/tools/vendor/google.golang.org/protobuf/internal/impl/codec_field.go b/tools/vendor/google.golang.org/protobuf/internal/impl/codec_field.go index 3fadd241e1..78ee47e44b 100644 --- a/tools/vendor/google.golang.org/protobuf/internal/impl/codec_field.go +++ b/tools/vendor/google.golang.org/protobuf/internal/impl/codec_field.go @@ -233,9 +233,15 @@ func sizeMessageInfo(p pointer, f *coderFieldInfo, opts marshalOptions) int { } func appendMessageInfo(b []byte, p pointer, f *coderFieldInfo, opts marshalOptions) ([]byte, error) { + calculatedSize := f.mi.sizePointer(p.Elem(), opts) b = protowire.AppendVarint(b, f.wiretag) - b = protowire.AppendVarint(b, uint64(f.mi.sizePointer(p.Elem(), opts))) - return f.mi.marshalAppendPointer(b, p.Elem(), opts) + b = protowire.AppendVarint(b, uint64(calculatedSize)) + before := len(b) + b, err := f.mi.marshalAppendPointer(b, p.Elem(), opts) + if measuredSize := len(b) - before; calculatedSize != measuredSize && err == nil { + return nil, errors.MismatchedSizeCalculation(calculatedSize, measuredSize) + } + return b, err } func consumeMessageInfo(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) { @@ -262,14 +268,21 @@ func isInitMessageInfo(p pointer, f *coderFieldInfo) error { return f.mi.checkInitializedPointer(p.Elem()) } -func sizeMessage(m proto.Message, tagsize int, _ marshalOptions) int { - return protowire.SizeBytes(proto.Size(m)) + tagsize +func sizeMessage(m proto.Message, tagsize int, opts marshalOptions) int { + return protowire.SizeBytes(opts.Options().Size(m)) + tagsize } func appendMessage(b []byte, m proto.Message, wiretag uint64, opts marshalOptions) ([]byte, error) { + mopts := opts.Options() + calculatedSize := mopts.Size(m) b = protowire.AppendVarint(b, wiretag) - b = protowire.AppendVarint(b, uint64(proto.Size(m))) - return opts.Options().MarshalAppend(b, m) + b = protowire.AppendVarint(b, uint64(calculatedSize)) + before := len(b) + b, err := mopts.MarshalAppend(b, m) + if measuredSize := len(b) - before; calculatedSize != measuredSize && err == nil { + return nil, errors.MismatchedSizeCalculation(calculatedSize, measuredSize) + } + return b, err } func consumeMessage(b []byte, m proto.Message, wtyp protowire.Type, opts unmarshalOptions) (out unmarshalOutput, err error) { @@ -405,8 +418,8 @@ func consumeGroupType(b []byte, p pointer, wtyp protowire.Type, f *coderFieldInf return f.mi.unmarshalPointer(b, p.Elem(), f.num, opts) } -func sizeGroup(m proto.Message, tagsize int, _ marshalOptions) int { - return 2*tagsize + proto.Size(m) +func sizeGroup(m proto.Message, tagsize int, opts marshalOptions) int { + return 2*tagsize + opts.Options().Size(m) } func appendGroup(b []byte, m proto.Message, wiretag uint64, opts marshalOptions) ([]byte, error) { @@ -482,10 +495,14 @@ func appendMessageSliceInfo(b []byte, p pointer, f *coderFieldInfo, opts marshal b = protowire.AppendVarint(b, f.wiretag) siz := f.mi.sizePointer(v, opts) b = protowire.AppendVarint(b, uint64(siz)) + before := len(b) b, err = f.mi.marshalAppendPointer(b, v, opts) if err != nil { return b, err } + if measuredSize := len(b) - before; siz != measuredSize { + return nil, errors.MismatchedSizeCalculation(siz, measuredSize) + } } return b, nil } @@ -520,28 +537,34 @@ func isInitMessageSliceInfo(p pointer, f *coderFieldInfo) error { return nil } -func sizeMessageSlice(p pointer, goType reflect.Type, tagsize int, _ marshalOptions) int { +func sizeMessageSlice(p pointer, goType reflect.Type, tagsize int, opts marshalOptions) int { + mopts := opts.Options() s := p.PointerSlice() n := 0 for _, v := range s { m := asMessage(v.AsValueOf(goType.Elem())) - n += protowire.SizeBytes(proto.Size(m)) + tagsize + n += protowire.SizeBytes(mopts.Size(m)) + tagsize } return n } func appendMessageSlice(b []byte, p pointer, wiretag uint64, goType reflect.Type, opts marshalOptions) ([]byte, error) { + mopts := opts.Options() s := p.PointerSlice() var err error for _, v := range s { m := asMessage(v.AsValueOf(goType.Elem())) b = protowire.AppendVarint(b, wiretag) - siz := proto.Size(m) + siz := mopts.Size(m) b = protowire.AppendVarint(b, uint64(siz)) - b, err = opts.Options().MarshalAppend(b, m) + before := len(b) + b, err = mopts.MarshalAppend(b, m) if err != nil { return b, err } + if measuredSize := len(b) - before; siz != measuredSize { + return nil, errors.MismatchedSizeCalculation(siz, measuredSize) + } } return b, nil } @@ -582,11 +605,12 @@ func isInitMessageSlice(p pointer, goType reflect.Type) error { // Slices of messages func sizeMessageSliceValue(listv protoreflect.Value, tagsize int, opts marshalOptions) int { + mopts := opts.Options() list := listv.List() n := 0 for i, llen := 0, list.Len(); i < llen; i++ { m := list.Get(i).Message().Interface() - n += protowire.SizeBytes(proto.Size(m)) + tagsize + n += protowire.SizeBytes(mopts.Size(m)) + tagsize } return n } @@ -597,13 +621,17 @@ func appendMessageSliceValue(b []byte, listv protoreflect.Value, wiretag uint64, for i, llen := 0, list.Len(); i < llen; i++ { m := list.Get(i).Message().Interface() b = protowire.AppendVarint(b, wiretag) - siz := proto.Size(m) + siz := mopts.Size(m) b = protowire.AppendVarint(b, uint64(siz)) + before := len(b) var err error b, err = mopts.MarshalAppend(b, m) if err != nil { return b, err } + if measuredSize := len(b) - before; siz != measuredSize { + return nil, errors.MismatchedSizeCalculation(siz, measuredSize) + } } return b, nil } @@ -651,11 +679,12 @@ var coderMessageSliceValue = valueCoderFuncs{ } func sizeGroupSliceValue(listv protoreflect.Value, tagsize int, opts marshalOptions) int { + mopts := opts.Options() list := listv.List() n := 0 for i, llen := 0, list.Len(); i < llen; i++ { m := list.Get(i).Message().Interface() - n += 2*tagsize + proto.Size(m) + n += 2*tagsize + mopts.Size(m) } return n } @@ -738,12 +767,13 @@ func makeGroupSliceFieldCoder(fd protoreflect.FieldDescriptor, ft reflect.Type) } } -func sizeGroupSlice(p pointer, messageType reflect.Type, tagsize int, _ marshalOptions) int { +func sizeGroupSlice(p pointer, messageType reflect.Type, tagsize int, opts marshalOptions) int { + mopts := opts.Options() s := p.PointerSlice() n := 0 for _, v := range s { m := asMessage(v.AsValueOf(messageType.Elem())) - n += 2*tagsize + proto.Size(m) + n += 2*tagsize + mopts.Size(m) } return n } diff --git a/tools/vendor/google.golang.org/protobuf/internal/impl/codec_map.go b/tools/vendor/google.golang.org/protobuf/internal/impl/codec_map.go index 111b9d16f9..fb35f0bae9 100644 --- a/tools/vendor/google.golang.org/protobuf/internal/impl/codec_map.go +++ b/tools/vendor/google.golang.org/protobuf/internal/impl/codec_map.go @@ -9,6 +9,7 @@ import ( "sort" "google.golang.org/protobuf/encoding/protowire" + "google.golang.org/protobuf/internal/errors" "google.golang.org/protobuf/internal/genid" "google.golang.org/protobuf/reflect/protoreflect" ) @@ -240,11 +241,16 @@ func appendMapItem(b []byte, keyrv, valrv reflect.Value, mapi *mapInfo, f *coder size += mapi.keyFuncs.size(key.Value(), mapKeyTagSize, opts) size += mapi.valFuncs.size(val, mapValTagSize, opts) b = protowire.AppendVarint(b, uint64(size)) + before := len(b) b, err := mapi.keyFuncs.marshal(b, key.Value(), mapi.keyWiretag, opts) if err != nil { return nil, err } - return mapi.valFuncs.marshal(b, val, mapi.valWiretag, opts) + b, err = mapi.valFuncs.marshal(b, val, mapi.valWiretag, opts) + if measuredSize := len(b) - before; size != measuredSize && err == nil { + return nil, errors.MismatchedSizeCalculation(size, measuredSize) + } + return b, err } else { key := mapi.conv.keyConv.PBValueOf(keyrv).MapKey() val := pointerOfValue(valrv) @@ -259,7 +265,12 @@ func appendMapItem(b []byte, keyrv, valrv reflect.Value, mapi *mapInfo, f *coder } b = protowire.AppendVarint(b, mapi.valWiretag) b = protowire.AppendVarint(b, uint64(valSize)) - return f.mi.marshalAppendPointer(b, val, opts) + before := len(b) + b, err = f.mi.marshalAppendPointer(b, val, opts) + if measuredSize := len(b) - before; valSize != measuredSize && err == nil { + return nil, errors.MismatchedSizeCalculation(valSize, measuredSize) + } + return b, err } } diff --git a/tools/vendor/google.golang.org/protobuf/internal/impl/codec_messageset.go b/tools/vendor/google.golang.org/protobuf/internal/impl/codec_messageset.go index b7a23faf1e..7a16ec13dd 100644 --- a/tools/vendor/google.golang.org/protobuf/internal/impl/codec_messageset.go +++ b/tools/vendor/google.golang.org/protobuf/internal/impl/codec_messageset.go @@ -26,6 +26,15 @@ func sizeMessageSet(mi *MessageInfo, p pointer, opts marshalOptions) (size int) } num, _ := protowire.DecodeTag(xi.wiretag) size += messageset.SizeField(num) + if fullyLazyExtensions(opts) { + // Don't expand the extension, instead use the buffer to calculate size + if lb := x.lazyBuffer(); lb != nil { + // We got hold of the buffer, so it's still lazy. + // Don't count the tag size in the extension buffer, it's already added. + size += protowire.SizeTag(messageset.FieldMessage) + len(lb) - xi.tagsize + continue + } + } size += xi.funcs.size(x.Value(), protowire.SizeTag(messageset.FieldMessage), opts) } @@ -85,6 +94,19 @@ func marshalMessageSetField(mi *MessageInfo, b []byte, x ExtensionField, opts ma xi := getExtensionFieldInfo(x.Type()) num, _ := protowire.DecodeTag(xi.wiretag) b = messageset.AppendFieldStart(b, num) + + if fullyLazyExtensions(opts) { + // Don't expand the extension if it's still in wire format, instead use the buffer content. + if lb := x.lazyBuffer(); lb != nil { + // The tag inside the lazy buffer is a different tag (the extension + // number), but what we need here is the tag for FieldMessage: + b = protowire.AppendVarint(b, protowire.EncodeTag(messageset.FieldMessage, protowire.BytesType)) + b = append(b, lb[xi.tagsize:]...) + b = messageset.AppendFieldEnd(b) + return b, nil + } + } + b, err := xi.funcs.marshal(b, x.Value(), protowire.EncodeTag(messageset.FieldMessage, protowire.BytesType), opts) if err != nil { return b, err diff --git a/tools/vendor/google.golang.org/protobuf/internal/impl/convert.go b/tools/vendor/google.golang.org/protobuf/internal/impl/convert.go index 185ef2efa5..e06ece55a2 100644 --- a/tools/vendor/google.golang.org/protobuf/internal/impl/convert.go +++ b/tools/vendor/google.golang.org/protobuf/internal/impl/convert.go @@ -14,7 +14,7 @@ import ( // unwrapper unwraps the value to the underlying value. // This is implemented by List and Map. type unwrapper interface { - protoUnwrap() interface{} + protoUnwrap() any } // A Converter coverts to/from Go reflect.Value types and protobuf protoreflect.Value types. diff --git a/tools/vendor/google.golang.org/protobuf/internal/impl/convert_list.go b/tools/vendor/google.golang.org/protobuf/internal/impl/convert_list.go index f89136516f..18cb96fd70 100644 --- a/tools/vendor/google.golang.org/protobuf/internal/impl/convert_list.go +++ b/tools/vendor/google.golang.org/protobuf/internal/impl/convert_list.go @@ -136,6 +136,6 @@ func (ls *listReflect) NewElement() protoreflect.Value { func (ls *listReflect) IsValid() bool { return !ls.v.IsNil() } -func (ls *listReflect) protoUnwrap() interface{} { +func (ls *listReflect) protoUnwrap() any { return ls.v.Interface() } diff --git a/tools/vendor/google.golang.org/protobuf/internal/impl/convert_map.go b/tools/vendor/google.golang.org/protobuf/internal/impl/convert_map.go index f30b0a0576..304244a651 100644 --- a/tools/vendor/google.golang.org/protobuf/internal/impl/convert_map.go +++ b/tools/vendor/google.golang.org/protobuf/internal/impl/convert_map.go @@ -116,6 +116,6 @@ func (ms *mapReflect) NewValue() protoreflect.Value { func (ms *mapReflect) IsValid() bool { return !ms.v.IsNil() } -func (ms *mapReflect) protoUnwrap() interface{} { +func (ms *mapReflect) protoUnwrap() any { return ms.v.Interface() } diff --git a/tools/vendor/google.golang.org/protobuf/internal/impl/encode.go b/tools/vendor/google.golang.org/protobuf/internal/impl/encode.go index 845c67d6e7..febd212247 100644 --- a/tools/vendor/google.golang.org/protobuf/internal/impl/encode.go +++ b/tools/vendor/google.golang.org/protobuf/internal/impl/encode.go @@ -49,8 +49,11 @@ func (mi *MessageInfo) sizePointer(p pointer, opts marshalOptions) (size int) { return 0 } if opts.UseCachedSize() && mi.sizecacheOffset.IsValid() { - if size := atomic.LoadInt32(p.Apply(mi.sizecacheOffset).Int32()); size >= 0 { - return int(size) + // The size cache contains the size + 1, to allow the + // zero value to be invalid, while also allowing for a + // 0 size to be cached. + if size := atomic.LoadInt32(p.Apply(mi.sizecacheOffset).Int32()); size > 0 { + return int(size - 1) } } return mi.sizePointerSlow(p, opts) @@ -60,7 +63,7 @@ func (mi *MessageInfo) sizePointerSlow(p pointer, opts marshalOptions) (size int if flags.ProtoLegacy && mi.isMessageSet { size = sizeMessageSet(mi, p, opts) if mi.sizecacheOffset.IsValid() { - atomic.StoreInt32(p.Apply(mi.sizecacheOffset).Int32(), int32(size)) + atomic.StoreInt32(p.Apply(mi.sizecacheOffset).Int32(), int32(size+1)) } return size } @@ -84,13 +87,16 @@ func (mi *MessageInfo) sizePointerSlow(p pointer, opts marshalOptions) (size int } } if mi.sizecacheOffset.IsValid() { - if size > math.MaxInt32 { + if size > (math.MaxInt32 - 1) { // The size is too large for the int32 sizecache field. // We will need to recompute the size when encoding; // unfortunately expensive, but better than invalid output. - atomic.StoreInt32(p.Apply(mi.sizecacheOffset).Int32(), -1) + atomic.StoreInt32(p.Apply(mi.sizecacheOffset).Int32(), 0) } else { - atomic.StoreInt32(p.Apply(mi.sizecacheOffset).Int32(), int32(size)) + // The size cache contains the size + 1, to allow the + // zero value to be invalid, while also allowing for a + // 0 size to be cached. + atomic.StoreInt32(p.Apply(mi.sizecacheOffset).Int32(), int32(size+1)) } } return size @@ -149,6 +155,14 @@ func (mi *MessageInfo) marshalAppendPointer(b []byte, p pointer, opts marshalOpt return b, nil } +// fullyLazyExtensions returns true if we should attempt to keep extensions lazy over size and marshal. +func fullyLazyExtensions(opts marshalOptions) bool { + // When deterministic marshaling is requested, force an unmarshal for lazy + // extensions to produce a deterministic result, instead of passing through + // bytes lazily that may or may not match what Go Protobuf would produce. + return opts.flags&piface.MarshalDeterministic == 0 +} + func (mi *MessageInfo) sizeExtensions(ext *map[int32]ExtensionField, opts marshalOptions) (n int) { if ext == nil { return 0 @@ -158,6 +172,14 @@ func (mi *MessageInfo) sizeExtensions(ext *map[int32]ExtensionField, opts marsha if xi.funcs.size == nil { continue } + if fullyLazyExtensions(opts) { + // Don't expand the extension, instead use the buffer to calculate size + if lb := x.lazyBuffer(); lb != nil { + // We got hold of the buffer, so it's still lazy. + n += len(lb) + continue + } + } n += xi.funcs.size(x.Value(), xi.tagsize, opts) } return n @@ -176,6 +198,13 @@ func (mi *MessageInfo) appendExtensions(b []byte, ext *map[int32]ExtensionField, var err error for _, x := range *ext { xi := getExtensionFieldInfo(x.Type()) + if fullyLazyExtensions(opts) { + // Don't expand the extension if it's still in wire format, instead use the buffer content. + if lb := x.lazyBuffer(); lb != nil { + b = append(b, lb...) + continue + } + } b, err = xi.funcs.marshal(b, x.Value(), xi.wiretag, opts) } return b, err @@ -191,6 +220,13 @@ func (mi *MessageInfo) appendExtensions(b []byte, ext *map[int32]ExtensionField, for _, k := range keys { x := (*ext)[int32(k)] xi := getExtensionFieldInfo(x.Type()) + if fullyLazyExtensions(opts) { + // Don't expand the extension if it's still in wire format, instead use the buffer content. + if lb := x.lazyBuffer(); lb != nil { + b = append(b, lb...) + continue + } + } b, err = xi.funcs.marshal(b, x.Value(), xi.wiretag, opts) if err != nil { return b, err diff --git a/tools/vendor/google.golang.org/protobuf/internal/impl/extension.go b/tools/vendor/google.golang.org/protobuf/internal/impl/extension.go index cb25b0bae1..e31249f64f 100644 --- a/tools/vendor/google.golang.org/protobuf/internal/impl/extension.go +++ b/tools/vendor/google.golang.org/protobuf/internal/impl/extension.go @@ -53,7 +53,7 @@ type ExtensionInfo struct { // type returned by InterfaceOf may not be identical. // // Deprecated: Use InterfaceOf(xt.Zero()) instead. - ExtensionType interface{} + ExtensionType any // Field is the field number of the extension. // @@ -95,16 +95,16 @@ func (xi *ExtensionInfo) New() protoreflect.Value { func (xi *ExtensionInfo) Zero() protoreflect.Value { return xi.lazyInit().Zero() } -func (xi *ExtensionInfo) ValueOf(v interface{}) protoreflect.Value { +func (xi *ExtensionInfo) ValueOf(v any) protoreflect.Value { return xi.lazyInit().PBValueOf(reflect.ValueOf(v)) } -func (xi *ExtensionInfo) InterfaceOf(v protoreflect.Value) interface{} { +func (xi *ExtensionInfo) InterfaceOf(v protoreflect.Value) any { return xi.lazyInit().GoValueOf(v).Interface() } func (xi *ExtensionInfo) IsValidValue(v protoreflect.Value) bool { return xi.lazyInit().IsValidPB(v) } -func (xi *ExtensionInfo) IsValidInterface(v interface{}) bool { +func (xi *ExtensionInfo) IsValidInterface(v any) bool { return xi.lazyInit().IsValidGo(reflect.ValueOf(v)) } func (xi *ExtensionInfo) TypeDescriptor() protoreflect.ExtensionTypeDescriptor { diff --git a/tools/vendor/google.golang.org/protobuf/internal/impl/legacy_enum.go b/tools/vendor/google.golang.org/protobuf/internal/impl/legacy_enum.go index c2a803bb2f..81b2b1a763 100644 --- a/tools/vendor/google.golang.org/protobuf/internal/impl/legacy_enum.go +++ b/tools/vendor/google.golang.org/protobuf/internal/impl/legacy_enum.go @@ -97,7 +97,7 @@ func (e *legacyEnumWrapper) Number() protoreflect.EnumNumber { func (e *legacyEnumWrapper) ProtoReflect() protoreflect.Enum { return e } -func (e *legacyEnumWrapper) protoUnwrap() interface{} { +func (e *legacyEnumWrapper) protoUnwrap() any { v := reflect.New(e.goTyp).Elem() v.SetInt(int64(e.num)) return v.Interface() @@ -167,6 +167,7 @@ func aberrantLoadEnumDesc(t reflect.Type) protoreflect.EnumDescriptor { ed := &filedesc.Enum{L2: new(filedesc.EnumL2)} ed.L0.FullName = AberrantDeriveFullName(t) // e.g., github_com.user.repo.MyEnum ed.L0.ParentFile = filedesc.SurrogateProto3 + ed.L1.EditionFeatures = ed.L0.ParentFile.L1.EditionFeatures ed.L2.Values.List = append(ed.L2.Values.List, filedesc.EnumValue{}) // TODO: Use the presence of a UnmarshalJSON method to determine proto2? diff --git a/tools/vendor/google.golang.org/protobuf/internal/impl/legacy_extension.go b/tools/vendor/google.golang.org/protobuf/internal/impl/legacy_extension.go index 87b30d0504..6e8677ee63 100644 --- a/tools/vendor/google.golang.org/protobuf/internal/impl/legacy_extension.go +++ b/tools/vendor/google.golang.org/protobuf/internal/impl/legacy_extension.go @@ -118,7 +118,7 @@ func (xi *ExtensionInfo) initFromLegacy() { xd.L1.Number = protoreflect.FieldNumber(xi.Field) xd.L1.Cardinality = fd.L1.Cardinality xd.L1.Kind = fd.L1.Kind - xd.L2.IsPacked = fd.L1.IsPacked + xd.L1.EditionFeatures = fd.L1.EditionFeatures xd.L2.Default = fd.L1.Default xd.L1.Extendee = Export{}.MessageDescriptorOf(xi.ExtendedType) xd.L2.Enum = ed diff --git a/tools/vendor/google.golang.org/protobuf/internal/impl/legacy_file.go b/tools/vendor/google.golang.org/protobuf/internal/impl/legacy_file.go index 9ab091086c..b649f1124b 100644 --- a/tools/vendor/google.golang.org/protobuf/internal/impl/legacy_file.go +++ b/tools/vendor/google.golang.org/protobuf/internal/impl/legacy_file.go @@ -7,7 +7,7 @@ package impl import ( "bytes" "compress/gzip" - "io/ioutil" + "io" "sync" "google.golang.org/protobuf/internal/filedesc" @@ -51,7 +51,7 @@ func legacyLoadFileDesc(b []byte) protoreflect.FileDescriptor { if err != nil { panic(err) } - b2, err := ioutil.ReadAll(zr) + b2, err := io.ReadAll(zr) if err != nil { panic(err) } diff --git a/tools/vendor/google.golang.org/protobuf/internal/impl/legacy_message.go b/tools/vendor/google.golang.org/protobuf/internal/impl/legacy_message.go index 2ab2c62978..bf0b6049b4 100644 --- a/tools/vendor/google.golang.org/protobuf/internal/impl/legacy_message.go +++ b/tools/vendor/google.golang.org/protobuf/internal/impl/legacy_message.go @@ -204,6 +204,7 @@ func aberrantLoadMessageDescReentrant(t reflect.Type, name protoreflect.FullName } } + md.L1.EditionFeatures = md.L0.ParentFile.L1.EditionFeatures // Obtain a list of oneof wrapper types. var oneofWrappers []reflect.Type methods := make([]reflect.Method, 0, 2) @@ -215,7 +216,7 @@ func aberrantLoadMessageDescReentrant(t reflect.Type, name protoreflect.FullName } for _, fn := range methods { for _, v := range fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))}) { - if vs, ok := v.Interface().([]interface{}); ok { + if vs, ok := v.Interface().([]any); ok { for _, v := range vs { oneofWrappers = append(oneofWrappers, reflect.TypeOf(v)) } @@ -250,6 +251,7 @@ func aberrantLoadMessageDescReentrant(t reflect.Type, name protoreflect.FullName od := &md.L2.Oneofs.List[n] od.L0.FullName = md.FullName().Append(protoreflect.Name(tag)) od.L0.ParentFile = md.L0.ParentFile + od.L1.EditionFeatures = md.L1.EditionFeatures od.L0.Parent = md od.L0.Index = n @@ -260,6 +262,7 @@ func aberrantLoadMessageDescReentrant(t reflect.Type, name protoreflect.FullName aberrantAppendField(md, f.Type, tag, "", "") fd := &md.L2.Fields.List[len(md.L2.Fields.List)-1] fd.L1.ContainingOneof = od + fd.L1.EditionFeatures = od.L1.EditionFeatures od.L1.Fields.List = append(od.L1.Fields.List, fd) } } @@ -307,14 +310,14 @@ func aberrantAppendField(md *filedesc.Message, goType reflect.Type, tag, tagKey, fd.L0.Parent = md fd.L0.Index = n - if fd.L1.IsWeak || fd.L1.HasPacked { + if fd.L1.IsWeak || fd.L1.EditionFeatures.IsPacked { fd.L1.Options = func() protoreflect.ProtoMessage { opts := descopts.Field.ProtoReflect().New() if fd.L1.IsWeak { opts.Set(opts.Descriptor().Fields().ByName("weak"), protoreflect.ValueOfBool(true)) } - if fd.L1.HasPacked { - opts.Set(opts.Descriptor().Fields().ByName("packed"), protoreflect.ValueOfBool(fd.L1.IsPacked)) + if fd.L1.EditionFeatures.IsPacked { + opts.Set(opts.Descriptor().Fields().ByName("packed"), protoreflect.ValueOfBool(fd.L1.EditionFeatures.IsPacked)) } return opts.Interface() } @@ -344,6 +347,7 @@ func aberrantAppendField(md *filedesc.Message, goType reflect.Type, tag, tagKey, md2.L0.ParentFile = md.L0.ParentFile md2.L0.Parent = md md2.L0.Index = n + md2.L1.EditionFeatures = md.L1.EditionFeatures md2.L1.IsMapEntry = true md2.L2.Options = func() protoreflect.ProtoMessage { @@ -563,6 +567,6 @@ func (m aberrantMessage) IsValid() bool { func (m aberrantMessage) ProtoMethods() *protoiface.Methods { return aberrantProtoMethods } -func (m aberrantMessage) protoUnwrap() interface{} { +func (m aberrantMessage) protoUnwrap() any { return m.v.Interface() } diff --git a/tools/vendor/google.golang.org/protobuf/internal/impl/message.go b/tools/vendor/google.golang.org/protobuf/internal/impl/message.go index 629bacdced..019399d454 100644 --- a/tools/vendor/google.golang.org/protobuf/internal/impl/message.go +++ b/tools/vendor/google.golang.org/protobuf/internal/impl/message.go @@ -35,7 +35,7 @@ type MessageInfo struct { Exporter exporter // OneofWrappers is list of pointers to oneof wrapper struct types. - OneofWrappers []interface{} + OneofWrappers []any initMu sync.Mutex // protects all unexported fields initDone uint32 @@ -47,7 +47,7 @@ type MessageInfo struct { // exporter is a function that returns a reference to the ith field of v, // where v is a pointer to a struct. It returns nil if it does not support // exporting the requested field (e.g., already exported). -type exporter func(v interface{}, i int) interface{} +type exporter func(v any, i int) any // getMessageInfo returns the MessageInfo for any message type that // is generated by our implementation of protoc-gen-go (for v2 and on). @@ -201,7 +201,7 @@ fieldLoop: } for _, fn := range methods { for _, v := range fn.Func.Call([]reflect.Value{reflect.Zero(fn.Type.In(0))}) { - if vs, ok := v.Interface().([]interface{}); ok { + if vs, ok := v.Interface().([]any); ok { oneofWrappers = vs } } @@ -256,7 +256,7 @@ func (mi *MessageInfo) Message(i int) protoreflect.MessageType { type mapEntryType struct { desc protoreflect.MessageDescriptor - valType interface{} // zero value of enum or message type + valType any // zero value of enum or message type } func (mt mapEntryType) New() protoreflect.Message { diff --git a/tools/vendor/google.golang.org/protobuf/internal/impl/message_reflect.go b/tools/vendor/google.golang.org/protobuf/internal/impl/message_reflect.go index d9ea010bef..ecb4623d70 100644 --- a/tools/vendor/google.golang.org/protobuf/internal/impl/message_reflect.go +++ b/tools/vendor/google.golang.org/protobuf/internal/impl/message_reflect.go @@ -20,7 +20,7 @@ type reflectMessageInfo struct { // fieldTypes contains the zero value of an enum or message field. // For lists, it contains the element type. // For maps, it contains the entry value type. - fieldTypes map[protoreflect.FieldNumber]interface{} + fieldTypes map[protoreflect.FieldNumber]any // denseFields is a subset of fields where: // 0 < fieldDesc.Number() < len(denseFields) @@ -28,7 +28,7 @@ type reflectMessageInfo struct { denseFields []*fieldInfo // rangeInfos is a list of all fields (not belonging to a oneof) and oneofs. - rangeInfos []interface{} // either *fieldInfo or *oneofInfo + rangeInfos []any // either *fieldInfo or *oneofInfo getUnknown func(pointer) protoreflect.RawFields setUnknown func(pointer, protoreflect.RawFields) @@ -224,7 +224,7 @@ func (mi *MessageInfo) makeFieldTypes(si structInfo) { } if ft != nil { if mi.fieldTypes == nil { - mi.fieldTypes = make(map[protoreflect.FieldNumber]interface{}) + mi.fieldTypes = make(map[protoreflect.FieldNumber]any) } mi.fieldTypes[fd.Number()] = reflect.Zero(ft).Interface() } @@ -247,39 +247,39 @@ func (m *extensionMap) Range(f func(protoreflect.FieldDescriptor, protoreflect.V } } } -func (m *extensionMap) Has(xt protoreflect.ExtensionType) (ok bool) { +func (m *extensionMap) Has(xd protoreflect.ExtensionTypeDescriptor) (ok bool) { if m == nil { return false } - xd := xt.TypeDescriptor() x, ok := (*m)[int32(xd.Number())] if !ok { return false } + if x.isUnexpandedLazy() { + // Avoid calling x.Value(), which triggers a lazy unmarshal. + return true + } switch { case xd.IsList(): return x.Value().List().Len() > 0 case xd.IsMap(): return x.Value().Map().Len() > 0 - case xd.Message() != nil: - return x.Value().Message().IsValid() } return true } -func (m *extensionMap) Clear(xt protoreflect.ExtensionType) { - delete(*m, int32(xt.TypeDescriptor().Number())) +func (m *extensionMap) Clear(xd protoreflect.ExtensionTypeDescriptor) { + delete(*m, int32(xd.Number())) } -func (m *extensionMap) Get(xt protoreflect.ExtensionType) protoreflect.Value { - xd := xt.TypeDescriptor() +func (m *extensionMap) Get(xd protoreflect.ExtensionTypeDescriptor) protoreflect.Value { if m != nil { if x, ok := (*m)[int32(xd.Number())]; ok { return x.Value() } } - return xt.Zero() + return xd.Type().Zero() } -func (m *extensionMap) Set(xt protoreflect.ExtensionType, v protoreflect.Value) { - xd := xt.TypeDescriptor() +func (m *extensionMap) Set(xd protoreflect.ExtensionTypeDescriptor, v protoreflect.Value) { + xt := xd.Type() isValid := true switch { case !xt.IsValidValue(v): @@ -292,7 +292,7 @@ func (m *extensionMap) Set(xt protoreflect.ExtensionType, v protoreflect.Value) isValid = v.Message().IsValid() } if !isValid { - panic(fmt.Sprintf("%v: assigning invalid value", xt.TypeDescriptor().FullName())) + panic(fmt.Sprintf("%v: assigning invalid value", xd.FullName())) } if *m == nil { @@ -302,16 +302,15 @@ func (m *extensionMap) Set(xt protoreflect.ExtensionType, v protoreflect.Value) x.Set(xt, v) (*m)[int32(xd.Number())] = x } -func (m *extensionMap) Mutable(xt protoreflect.ExtensionType) protoreflect.Value { - xd := xt.TypeDescriptor() +func (m *extensionMap) Mutable(xd protoreflect.ExtensionTypeDescriptor) protoreflect.Value { if xd.Kind() != protoreflect.MessageKind && xd.Kind() != protoreflect.GroupKind && !xd.IsList() && !xd.IsMap() { panic("invalid Mutable on field with non-composite type") } if x, ok := (*m)[int32(xd.Number())]; ok { return x.Value() } - v := xt.New() - m.Set(xt, v) + v := xd.Type().New() + m.Set(xd, v) return v } @@ -394,7 +393,7 @@ var ( // MessageOf returns a reflective view over a message. The input must be a // pointer to a named Go struct. If the provided type has a ProtoReflect method, // it must be implemented by calling this method. -func (mi *MessageInfo) MessageOf(m interface{}) protoreflect.Message { +func (mi *MessageInfo) MessageOf(m any) protoreflect.Message { if reflect.TypeOf(m) != mi.GoReflectType { panic(fmt.Sprintf("type mismatch: got %T, want %v", m, mi.GoReflectType)) } @@ -422,13 +421,13 @@ func (m *messageIfaceWrapper) Reset() { func (m *messageIfaceWrapper) ProtoReflect() protoreflect.Message { return (*messageReflectWrapper)(m) } -func (m *messageIfaceWrapper) protoUnwrap() interface{} { +func (m *messageIfaceWrapper) protoUnwrap() any { return m.p.AsIfaceOf(m.mi.GoReflectType.Elem()) } // checkField verifies that the provided field descriptor is valid. // Exactly one of the returned values is populated. -func (mi *MessageInfo) checkField(fd protoreflect.FieldDescriptor) (*fieldInfo, protoreflect.ExtensionType) { +func (mi *MessageInfo) checkField(fd protoreflect.FieldDescriptor) (*fieldInfo, protoreflect.ExtensionTypeDescriptor) { var fi *fieldInfo if n := fd.Number(); 0 < n && int(n) < len(mi.denseFields) { fi = mi.denseFields[n] @@ -457,7 +456,7 @@ func (mi *MessageInfo) checkField(fd protoreflect.FieldDescriptor) (*fieldInfo, if !ok { panic(fmt.Sprintf("extension %v does not implement protoreflect.ExtensionTypeDescriptor", fd.FullName())) } - return nil, xtd.Type() + return nil, xtd } panic(fmt.Sprintf("field %v is invalid", fd.FullName())) } diff --git a/tools/vendor/google.golang.org/protobuf/internal/impl/message_reflect_gen.go b/tools/vendor/google.golang.org/protobuf/internal/impl/message_reflect_gen.go index 741d6e5b6b..99dc23c6f0 100644 --- a/tools/vendor/google.golang.org/protobuf/internal/impl/message_reflect_gen.go +++ b/tools/vendor/google.golang.org/protobuf/internal/impl/message_reflect_gen.go @@ -23,12 +23,13 @@ func (m *messageState) New() protoreflect.Message { func (m *messageState) Interface() protoreflect.ProtoMessage { return m.protoUnwrap().(protoreflect.ProtoMessage) } -func (m *messageState) protoUnwrap() interface{} { +func (m *messageState) protoUnwrap() any { return m.pointer().AsIfaceOf(m.messageInfo().GoReflectType.Elem()) } func (m *messageState) ProtoMethods() *protoiface.Methods { - m.messageInfo().init() - return &m.messageInfo().methods + mi := m.messageInfo() + mi.init() + return &mi.methods } // ProtoMessageInfo is a pseudo-internal API for allowing the v1 code @@ -41,8 +42,9 @@ func (m *messageState) ProtoMessageInfo() *MessageInfo { } func (m *messageState) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) { - m.messageInfo().init() - for _, ri := range m.messageInfo().rangeInfos { + mi := m.messageInfo() + mi.init() + for _, ri := range mi.rangeInfos { switch ri := ri.(type) { case *fieldInfo: if ri.has(m.pointer()) { @@ -52,77 +54,86 @@ func (m *messageState) Range(f func(protoreflect.FieldDescriptor, protoreflect.V } case *oneofInfo: if n := ri.which(m.pointer()); n > 0 { - fi := m.messageInfo().fields[n] + fi := mi.fields[n] if !f(fi.fieldDesc, fi.get(m.pointer())) { return } } } } - m.messageInfo().extensionMap(m.pointer()).Range(f) + mi.extensionMap(m.pointer()).Range(f) } func (m *messageState) Has(fd protoreflect.FieldDescriptor) bool { - m.messageInfo().init() - if fi, xt := m.messageInfo().checkField(fd); fi != nil { + mi := m.messageInfo() + mi.init() + if fi, xd := mi.checkField(fd); fi != nil { return fi.has(m.pointer()) } else { - return m.messageInfo().extensionMap(m.pointer()).Has(xt) + return mi.extensionMap(m.pointer()).Has(xd) } } func (m *messageState) Clear(fd protoreflect.FieldDescriptor) { - m.messageInfo().init() - if fi, xt := m.messageInfo().checkField(fd); fi != nil { + mi := m.messageInfo() + mi.init() + if fi, xd := mi.checkField(fd); fi != nil { fi.clear(m.pointer()) } else { - m.messageInfo().extensionMap(m.pointer()).Clear(xt) + mi.extensionMap(m.pointer()).Clear(xd) } } func (m *messageState) Get(fd protoreflect.FieldDescriptor) protoreflect.Value { - m.messageInfo().init() - if fi, xt := m.messageInfo().checkField(fd); fi != nil { + mi := m.messageInfo() + mi.init() + if fi, xd := mi.checkField(fd); fi != nil { return fi.get(m.pointer()) } else { - return m.messageInfo().extensionMap(m.pointer()).Get(xt) + return mi.extensionMap(m.pointer()).Get(xd) } } func (m *messageState) Set(fd protoreflect.FieldDescriptor, v protoreflect.Value) { - m.messageInfo().init() - if fi, xt := m.messageInfo().checkField(fd); fi != nil { + mi := m.messageInfo() + mi.init() + if fi, xd := mi.checkField(fd); fi != nil { fi.set(m.pointer(), v) } else { - m.messageInfo().extensionMap(m.pointer()).Set(xt, v) + mi.extensionMap(m.pointer()).Set(xd, v) } } func (m *messageState) Mutable(fd protoreflect.FieldDescriptor) protoreflect.Value { - m.messageInfo().init() - if fi, xt := m.messageInfo().checkField(fd); fi != nil { + mi := m.messageInfo() + mi.init() + if fi, xd := mi.checkField(fd); fi != nil { return fi.mutable(m.pointer()) } else { - return m.messageInfo().extensionMap(m.pointer()).Mutable(xt) + return mi.extensionMap(m.pointer()).Mutable(xd) } } func (m *messageState) NewField(fd protoreflect.FieldDescriptor) protoreflect.Value { - m.messageInfo().init() - if fi, xt := m.messageInfo().checkField(fd); fi != nil { + mi := m.messageInfo() + mi.init() + if fi, xd := mi.checkField(fd); fi != nil { return fi.newField() } else { - return xt.New() + return xd.Type().New() } } func (m *messageState) WhichOneof(od protoreflect.OneofDescriptor) protoreflect.FieldDescriptor { - m.messageInfo().init() - if oi := m.messageInfo().oneofs[od.Name()]; oi != nil && oi.oneofDesc == od { + mi := m.messageInfo() + mi.init() + if oi := mi.oneofs[od.Name()]; oi != nil && oi.oneofDesc == od { return od.Fields().ByNumber(oi.which(m.pointer())) } panic("invalid oneof descriptor " + string(od.FullName()) + " for message " + string(m.Descriptor().FullName())) } func (m *messageState) GetUnknown() protoreflect.RawFields { - m.messageInfo().init() - return m.messageInfo().getUnknown(m.pointer()) + mi := m.messageInfo() + mi.init() + return mi.getUnknown(m.pointer()) } func (m *messageState) SetUnknown(b protoreflect.RawFields) { - m.messageInfo().init() - m.messageInfo().setUnknown(m.pointer(), b) + mi := m.messageInfo() + mi.init() + mi.setUnknown(m.pointer(), b) } func (m *messageState) IsValid() bool { return !m.pointer().IsNil() @@ -143,12 +154,13 @@ func (m *messageReflectWrapper) Interface() protoreflect.ProtoMessage { } return (*messageIfaceWrapper)(m) } -func (m *messageReflectWrapper) protoUnwrap() interface{} { +func (m *messageReflectWrapper) protoUnwrap() any { return m.pointer().AsIfaceOf(m.messageInfo().GoReflectType.Elem()) } func (m *messageReflectWrapper) ProtoMethods() *protoiface.Methods { - m.messageInfo().init() - return &m.messageInfo().methods + mi := m.messageInfo() + mi.init() + return &mi.methods } // ProtoMessageInfo is a pseudo-internal API for allowing the v1 code @@ -161,8 +173,9 @@ func (m *messageReflectWrapper) ProtoMessageInfo() *MessageInfo { } func (m *messageReflectWrapper) Range(f func(protoreflect.FieldDescriptor, protoreflect.Value) bool) { - m.messageInfo().init() - for _, ri := range m.messageInfo().rangeInfos { + mi := m.messageInfo() + mi.init() + for _, ri := range mi.rangeInfos { switch ri := ri.(type) { case *fieldInfo: if ri.has(m.pointer()) { @@ -172,77 +185,86 @@ func (m *messageReflectWrapper) Range(f func(protoreflect.FieldDescriptor, proto } case *oneofInfo: if n := ri.which(m.pointer()); n > 0 { - fi := m.messageInfo().fields[n] + fi := mi.fields[n] if !f(fi.fieldDesc, fi.get(m.pointer())) { return } } } } - m.messageInfo().extensionMap(m.pointer()).Range(f) + mi.extensionMap(m.pointer()).Range(f) } func (m *messageReflectWrapper) Has(fd protoreflect.FieldDescriptor) bool { - m.messageInfo().init() - if fi, xt := m.messageInfo().checkField(fd); fi != nil { + mi := m.messageInfo() + mi.init() + if fi, xd := mi.checkField(fd); fi != nil { return fi.has(m.pointer()) } else { - return m.messageInfo().extensionMap(m.pointer()).Has(xt) + return mi.extensionMap(m.pointer()).Has(xd) } } func (m *messageReflectWrapper) Clear(fd protoreflect.FieldDescriptor) { - m.messageInfo().init() - if fi, xt := m.messageInfo().checkField(fd); fi != nil { + mi := m.messageInfo() + mi.init() + if fi, xd := mi.checkField(fd); fi != nil { fi.clear(m.pointer()) } else { - m.messageInfo().extensionMap(m.pointer()).Clear(xt) + mi.extensionMap(m.pointer()).Clear(xd) } } func (m *messageReflectWrapper) Get(fd protoreflect.FieldDescriptor) protoreflect.Value { - m.messageInfo().init() - if fi, xt := m.messageInfo().checkField(fd); fi != nil { + mi := m.messageInfo() + mi.init() + if fi, xd := mi.checkField(fd); fi != nil { return fi.get(m.pointer()) } else { - return m.messageInfo().extensionMap(m.pointer()).Get(xt) + return mi.extensionMap(m.pointer()).Get(xd) } } func (m *messageReflectWrapper) Set(fd protoreflect.FieldDescriptor, v protoreflect.Value) { - m.messageInfo().init() - if fi, xt := m.messageInfo().checkField(fd); fi != nil { + mi := m.messageInfo() + mi.init() + if fi, xd := mi.checkField(fd); fi != nil { fi.set(m.pointer(), v) } else { - m.messageInfo().extensionMap(m.pointer()).Set(xt, v) + mi.extensionMap(m.pointer()).Set(xd, v) } } func (m *messageReflectWrapper) Mutable(fd protoreflect.FieldDescriptor) protoreflect.Value { - m.messageInfo().init() - if fi, xt := m.messageInfo().checkField(fd); fi != nil { + mi := m.messageInfo() + mi.init() + if fi, xd := mi.checkField(fd); fi != nil { return fi.mutable(m.pointer()) } else { - return m.messageInfo().extensionMap(m.pointer()).Mutable(xt) + return mi.extensionMap(m.pointer()).Mutable(xd) } } func (m *messageReflectWrapper) NewField(fd protoreflect.FieldDescriptor) protoreflect.Value { - m.messageInfo().init() - if fi, xt := m.messageInfo().checkField(fd); fi != nil { + mi := m.messageInfo() + mi.init() + if fi, xd := mi.checkField(fd); fi != nil { return fi.newField() } else { - return xt.New() + return xd.Type().New() } } func (m *messageReflectWrapper) WhichOneof(od protoreflect.OneofDescriptor) protoreflect.FieldDescriptor { - m.messageInfo().init() - if oi := m.messageInfo().oneofs[od.Name()]; oi != nil && oi.oneofDesc == od { + mi := m.messageInfo() + mi.init() + if oi := mi.oneofs[od.Name()]; oi != nil && oi.oneofDesc == od { return od.Fields().ByNumber(oi.which(m.pointer())) } panic("invalid oneof descriptor " + string(od.FullName()) + " for message " + string(m.Descriptor().FullName())) } func (m *messageReflectWrapper) GetUnknown() protoreflect.RawFields { - m.messageInfo().init() - return m.messageInfo().getUnknown(m.pointer()) + mi := m.messageInfo() + mi.init() + return mi.getUnknown(m.pointer()) } func (m *messageReflectWrapper) SetUnknown(b protoreflect.RawFields) { - m.messageInfo().init() - m.messageInfo().setUnknown(m.pointer(), b) + mi := m.messageInfo() + mi.init() + mi.setUnknown(m.pointer(), b) } func (m *messageReflectWrapper) IsValid() bool { return !m.pointer().IsNil() diff --git a/tools/vendor/google.golang.org/protobuf/internal/impl/pointer_reflect.go b/tools/vendor/google.golang.org/protobuf/internal/impl/pointer_reflect.go index 517e94434c..da685e8a29 100644 --- a/tools/vendor/google.golang.org/protobuf/internal/impl/pointer_reflect.go +++ b/tools/vendor/google.golang.org/protobuf/internal/impl/pointer_reflect.go @@ -16,7 +16,7 @@ import ( const UnsafeEnabled = false // Pointer is an opaque pointer type. -type Pointer interface{} +type Pointer any // offset represents the offset to a struct field, accessible from a pointer. // The offset is the field index into a struct. @@ -62,7 +62,7 @@ func pointerOfValue(v reflect.Value) pointer { } // pointerOfIface returns the pointer portion of an interface. -func pointerOfIface(v interface{}) pointer { +func pointerOfIface(v any) pointer { return pointer{v: reflect.ValueOf(v)} } @@ -93,7 +93,7 @@ func (p pointer) AsValueOf(t reflect.Type) reflect.Value { // AsIfaceOf treats p as a pointer to an object of type t and returns the value. // It is equivalent to p.AsValueOf(t).Interface() -func (p pointer) AsIfaceOf(t reflect.Type) interface{} { +func (p pointer) AsIfaceOf(t reflect.Type) any { return p.AsValueOf(t).Interface() } diff --git a/tools/vendor/google.golang.org/protobuf/internal/impl/pointer_unsafe.go b/tools/vendor/google.golang.org/protobuf/internal/impl/pointer_unsafe.go index 4b020e3116..5f20ca5d8a 100644 --- a/tools/vendor/google.golang.org/protobuf/internal/impl/pointer_unsafe.go +++ b/tools/vendor/google.golang.org/protobuf/internal/impl/pointer_unsafe.go @@ -50,7 +50,7 @@ func pointerOfValue(v reflect.Value) pointer { } // pointerOfIface returns the pointer portion of an interface. -func pointerOfIface(v interface{}) pointer { +func pointerOfIface(v any) pointer { type ifaceHeader struct { Type unsafe.Pointer Data unsafe.Pointer @@ -80,7 +80,7 @@ func (p pointer) AsValueOf(t reflect.Type) reflect.Value { // AsIfaceOf treats p as a pointer to an object of type t and returns the value. // It is equivalent to p.AsValueOf(t).Interface() -func (p pointer) AsIfaceOf(t reflect.Type) interface{} { +func (p pointer) AsIfaceOf(t reflect.Type) any { // TODO: Use tricky unsafe magic to directly create ifaceHeader. return p.AsValueOf(t).Interface() } diff --git a/tools/vendor/google.golang.org/protobuf/internal/order/range.go b/tools/vendor/google.golang.org/protobuf/internal/order/range.go index 1665a68e5b..a1f09162d0 100644 --- a/tools/vendor/google.golang.org/protobuf/internal/order/range.go +++ b/tools/vendor/google.golang.org/protobuf/internal/order/range.go @@ -18,7 +18,7 @@ type messageField struct { } var messageFieldPool = sync.Pool{ - New: func() interface{} { return new([]messageField) }, + New: func() any { return new([]messageField) }, } type ( @@ -69,7 +69,7 @@ type mapEntry struct { } var mapEntryPool = sync.Pool{ - New: func() interface{} { return new([]mapEntry) }, + New: func() any { return new([]mapEntry) }, } type ( diff --git a/tools/vendor/google.golang.org/protobuf/internal/version/version.go b/tools/vendor/google.golang.org/protobuf/internal/version/version.go index a50fcfb49b..dbbf1f6862 100644 --- a/tools/vendor/google.golang.org/protobuf/internal/version/version.go +++ b/tools/vendor/google.golang.org/protobuf/internal/version/version.go @@ -51,8 +51,8 @@ import ( // 10. Send out the CL for review and submit it. const ( Major = 1 - Minor = 33 - Patch = 0 + Minor = 34 + Patch = 2 PreRelease = "" ) diff --git a/tools/vendor/google.golang.org/protobuf/proto/decode.go b/tools/vendor/google.golang.org/protobuf/proto/decode.go index e5b03b5677..d75a6534c1 100644 --- a/tools/vendor/google.golang.org/protobuf/proto/decode.go +++ b/tools/vendor/google.golang.org/protobuf/proto/decode.go @@ -51,6 +51,8 @@ type UnmarshalOptions struct { // Unmarshal parses the wire-format message in b and places the result in m. // The provided message must be mutable (e.g., a non-nil pointer to a message). +// +// See the [UnmarshalOptions] type if you need more control. func Unmarshal(b []byte, m Message) error { _, err := UnmarshalOptions{RecursionLimit: protowire.DefaultRecursionLimit}.unmarshal(b, m.ProtoReflect()) return err diff --git a/tools/vendor/google.golang.org/protobuf/proto/encode.go b/tools/vendor/google.golang.org/protobuf/proto/encode.go index 4fed202f9f..1f847bcc35 100644 --- a/tools/vendor/google.golang.org/protobuf/proto/encode.go +++ b/tools/vendor/google.golang.org/protobuf/proto/encode.go @@ -5,12 +5,17 @@ package proto import ( + "errors" + "fmt" + "google.golang.org/protobuf/encoding/protowire" "google.golang.org/protobuf/internal/encoding/messageset" "google.golang.org/protobuf/internal/order" "google.golang.org/protobuf/internal/pragma" "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/runtime/protoiface" + + protoerrors "google.golang.org/protobuf/internal/errors" ) // MarshalOptions configures the marshaler. @@ -70,7 +75,32 @@ type MarshalOptions struct { UseCachedSize bool } +// flags turns the specified MarshalOptions (user-facing) into +// protoiface.MarshalInputFlags (used internally by the marshaler). +// +// See impl.marshalOptions.Options for the inverse operation. +func (o MarshalOptions) flags() protoiface.MarshalInputFlags { + var flags protoiface.MarshalInputFlags + + // Note: o.AllowPartial is always forced to true by MarshalOptions.marshal, + // which is why it is not a part of MarshalInputFlags. + + if o.Deterministic { + flags |= protoiface.MarshalDeterministic + } + + if o.UseCachedSize { + flags |= protoiface.MarshalUseCachedSize + } + + return flags +} + // Marshal returns the wire-format encoding of m. +// +// This is the most common entry point for encoding a Protobuf message. +// +// See the [MarshalOptions] type if you need more control. func Marshal(m Message) ([]byte, error) { // Treat nil message interface as an empty message; nothing to output. if m == nil { @@ -116,6 +146,9 @@ func emptyBytesForMessage(m Message) []byte { // MarshalAppend appends the wire-format encoding of m to b, // returning the result. +// +// This is a less common entry point than [Marshal], which is only needed if you +// need to supply your own buffers for performance reasons. func (o MarshalOptions) MarshalAppend(b []byte, m Message) ([]byte, error) { // Treat nil message interface as an empty message; nothing to append. if m == nil { @@ -145,12 +178,7 @@ func (o MarshalOptions) marshal(b []byte, m protoreflect.Message) (out protoifac in := protoiface.MarshalInput{ Message: m, Buf: b, - } - if o.Deterministic { - in.Flags |= protoiface.MarshalDeterministic - } - if o.UseCachedSize { - in.Flags |= protoiface.MarshalUseCachedSize + Flags: o.flags(), } if methods.Size != nil { sout := methods.Size(protoiface.SizeInput{ @@ -168,6 +196,10 @@ func (o MarshalOptions) marshal(b []byte, m protoreflect.Message) (out protoifac out.Buf, err = o.marshalMessageSlow(b, m) } if err != nil { + var mismatch *protoerrors.SizeMismatchError + if errors.As(err, &mismatch) { + return out, fmt.Errorf("marshaling %s: %v", string(m.Descriptor().FullName()), err) + } return out, err } if allowPartial { diff --git a/tools/vendor/google.golang.org/protobuf/proto/extension.go b/tools/vendor/google.golang.org/protobuf/proto/extension.go index 17899a3a76..d248f29284 100644 --- a/tools/vendor/google.golang.org/protobuf/proto/extension.go +++ b/tools/vendor/google.golang.org/protobuf/proto/extension.go @@ -11,18 +11,21 @@ import ( // HasExtension reports whether an extension field is populated. // It returns false if m is invalid or if xt does not extend m. func HasExtension(m Message, xt protoreflect.ExtensionType) bool { - // Treat nil message interface as an empty message; no populated fields. - if m == nil { + // Treat nil message interface or descriptor as an empty message; no populated + // fields. + if m == nil || xt == nil { return false } // As a special-case, we reports invalid or mismatching descriptors // as always not being populated (since they aren't). - if xt == nil || m.ProtoReflect().Descriptor() != xt.TypeDescriptor().ContainingMessage() { + mr := m.ProtoReflect() + xd := xt.TypeDescriptor() + if mr.Descriptor() != xd.ContainingMessage() { return false } - return m.ProtoReflect().Has(xt.TypeDescriptor()) + return mr.Has(xd) } // ClearExtension clears an extension field such that subsequent @@ -36,7 +39,7 @@ func ClearExtension(m Message, xt protoreflect.ExtensionType) { // If the field is unpopulated, it returns the default value for // scalars and an immutable, empty value for lists or messages. // It panics if xt does not extend m. -func GetExtension(m Message, xt protoreflect.ExtensionType) interface{} { +func GetExtension(m Message, xt protoreflect.ExtensionType) any { // Treat nil message interface as an empty message; return the default. if m == nil { return xt.InterfaceOf(xt.Zero()) @@ -48,7 +51,7 @@ func GetExtension(m Message, xt protoreflect.ExtensionType) interface{} { // SetExtension stores the value of an extension field. // It panics if m is invalid, xt does not extend m, or if type of v // is invalid for the specified extension field. -func SetExtension(m Message, xt protoreflect.ExtensionType, v interface{}) { +func SetExtension(m Message, xt protoreflect.ExtensionType, v any) { xd := xt.TypeDescriptor() pv := xt.ValueOf(v) @@ -75,7 +78,7 @@ func SetExtension(m Message, xt protoreflect.ExtensionType, v interface{}) { // It returns immediately if f returns false. // While iterating, mutating operations may only be performed // on the current extension field. -func RangeExtensions(m Message, f func(protoreflect.ExtensionType, interface{}) bool) { +func RangeExtensions(m Message, f func(protoreflect.ExtensionType, any) bool) { // Treat nil message interface as an empty message; nothing to range over. if m == nil { return diff --git a/tools/vendor/google.golang.org/protobuf/proto/messageset.go b/tools/vendor/google.golang.org/protobuf/proto/messageset.go index 312d5d45c6..575d14831f 100644 --- a/tools/vendor/google.golang.org/protobuf/proto/messageset.go +++ b/tools/vendor/google.golang.org/protobuf/proto/messageset.go @@ -47,11 +47,16 @@ func (o MarshalOptions) marshalMessageSet(b []byte, m protoreflect.Message) ([]b func (o MarshalOptions) marshalMessageSetField(b []byte, fd protoreflect.FieldDescriptor, value protoreflect.Value) ([]byte, error) { b = messageset.AppendFieldStart(b, fd.Number()) b = protowire.AppendTag(b, messageset.FieldMessage, protowire.BytesType) - b = protowire.AppendVarint(b, uint64(o.Size(value.Message().Interface()))) + calculatedSize := o.Size(value.Message().Interface()) + b = protowire.AppendVarint(b, uint64(calculatedSize)) + before := len(b) b, err := o.marshalMessage(b, value.Message()) if err != nil { return b, err } + if measuredSize := len(b) - before; calculatedSize != measuredSize { + return nil, errors.MismatchedSizeCalculation(calculatedSize, measuredSize) + } b = messageset.AppendFieldEnd(b) return b, nil } diff --git a/tools/vendor/google.golang.org/protobuf/proto/size.go b/tools/vendor/google.golang.org/protobuf/proto/size.go index f1692b49b6..052fb5ae31 100644 --- a/tools/vendor/google.golang.org/protobuf/proto/size.go +++ b/tools/vendor/google.golang.org/protobuf/proto/size.go @@ -34,6 +34,7 @@ func (o MarshalOptions) size(m protoreflect.Message) (size int) { if methods != nil && methods.Size != nil { out := methods.Size(protoiface.SizeInput{ Message: m, + Flags: o.flags(), }) return out.Size } @@ -42,6 +43,7 @@ func (o MarshalOptions) size(m protoreflect.Message) (size int) { // This case is mainly used for legacy types with a Marshal method. out, _ := methods.Marshal(protoiface.MarshalInput{ Message: m, + Flags: o.flags(), }) return len(out.Buf) } diff --git a/tools/vendor/google.golang.org/protobuf/reflect/protodesc/desc.go b/tools/vendor/google.golang.org/protobuf/reflect/protodesc/desc.go index baa0cc6218..8fbecb4f58 100644 --- a/tools/vendor/google.golang.org/protobuf/reflect/protodesc/desc.go +++ b/tools/vendor/google.golang.org/protobuf/reflect/protodesc/desc.go @@ -13,6 +13,7 @@ package protodesc import ( + "google.golang.org/protobuf/internal/editionssupport" "google.golang.org/protobuf/internal/errors" "google.golang.org/protobuf/internal/filedesc" "google.golang.org/protobuf/internal/pragma" @@ -91,15 +92,17 @@ func (o FileOptions) New(fd *descriptorpb.FileDescriptorProto, r Resolver) (prot switch fd.GetSyntax() { case "proto2", "": f.L1.Syntax = protoreflect.Proto2 + f.L1.Edition = filedesc.EditionProto2 case "proto3": f.L1.Syntax = protoreflect.Proto3 + f.L1.Edition = filedesc.EditionProto3 case "editions": f.L1.Syntax = protoreflect.Editions f.L1.Edition = fromEditionProto(fd.GetEdition()) default: return nil, errors.New("invalid syntax: %q", fd.GetSyntax()) } - if f.L1.Syntax == protoreflect.Editions && (fd.GetEdition() < SupportedEditionsMinimum || fd.GetEdition() > SupportedEditionsMaximum) { + if f.L1.Syntax == protoreflect.Editions && (fd.GetEdition() < editionssupport.Minimum || fd.GetEdition() > editionssupport.Maximum) { return nil, errors.New("use of edition %v not yet supported by the Go Protobuf runtime", fd.GetEdition()) } f.L1.Path = fd.GetName() @@ -114,9 +117,7 @@ func (o FileOptions) New(fd *descriptorpb.FileDescriptorProto, r Resolver) (prot opts = proto.Clone(opts).(*descriptorpb.FileOptions) f.L2.Options = func() protoreflect.ProtoMessage { return opts } } - if f.L1.Syntax == protoreflect.Editions { - initFileDescFromFeatureSet(f, fd.GetOptions().GetFeatures()) - } + initFileDescFromFeatureSet(f, fd.GetOptions().GetFeatures()) f.L2.Imports = make(filedesc.FileImports, len(fd.GetDependency())) for _, i := range fd.GetPublicDependency() { @@ -219,10 +220,10 @@ func (o FileOptions) New(fd *descriptorpb.FileDescriptorProto, r Resolver) (prot if err := validateEnumDeclarations(f.L1.Enums.List, fd.GetEnumType()); err != nil { return nil, err } - if err := validateMessageDeclarations(f.L1.Messages.List, fd.GetMessageType()); err != nil { + if err := validateMessageDeclarations(f, f.L1.Messages.List, fd.GetMessageType()); err != nil { return nil, err } - if err := validateExtensionDeclarations(f.L1.Extensions.List, fd.GetExtension()); err != nil { + if err := validateExtensionDeclarations(f, f.L1.Extensions.List, fd.GetExtension()); err != nil { return nil, err } diff --git a/tools/vendor/google.golang.org/protobuf/reflect/protodesc/desc_init.go b/tools/vendor/google.golang.org/protobuf/reflect/protodesc/desc_init.go index b3278163c5..8561755427 100644 --- a/tools/vendor/google.golang.org/protobuf/reflect/protodesc/desc_init.go +++ b/tools/vendor/google.golang.org/protobuf/reflect/protodesc/desc_init.go @@ -69,9 +69,7 @@ func (r descsByName) initMessagesDeclarations(mds []*descriptorpb.DescriptorProt if m.L0, err = r.makeBase(m, parent, md.GetName(), i, sb); err != nil { return nil, err } - if m.Base.L0.ParentFile.Syntax() == protoreflect.Editions { - m.L1.EditionFeatures = mergeEditionFeatures(parent, md.GetOptions().GetFeatures()) - } + m.L1.EditionFeatures = mergeEditionFeatures(parent, md.GetOptions().GetFeatures()) if opts := md.GetOptions(); opts != nil { opts = proto.Clone(opts).(*descriptorpb.MessageOptions) m.L2.Options = func() protoreflect.ProtoMessage { return opts } @@ -146,13 +144,15 @@ func (r descsByName) initFieldsFromDescriptorProto(fds []*descriptorpb.FieldDesc if f.L0, err = r.makeBase(f, parent, fd.GetName(), i, sb); err != nil { return nil, err } + f.L1.EditionFeatures = mergeEditionFeatures(parent, fd.GetOptions().GetFeatures()) f.L1.IsProto3Optional = fd.GetProto3Optional() if opts := fd.GetOptions(); opts != nil { opts = proto.Clone(opts).(*descriptorpb.FieldOptions) f.L1.Options = func() protoreflect.ProtoMessage { return opts } f.L1.IsWeak = opts.GetWeak() - f.L1.HasPacked = opts.Packed != nil - f.L1.IsPacked = opts.GetPacked() + if opts.Packed != nil { + f.L1.EditionFeatures.IsPacked = opts.GetPacked() + } } f.L1.Number = protoreflect.FieldNumber(fd.GetNumber()) f.L1.Cardinality = protoreflect.Cardinality(fd.GetLabel()) @@ -163,32 +163,12 @@ func (r descsByName) initFieldsFromDescriptorProto(fds []*descriptorpb.FieldDesc f.L1.StringName.InitJSON(fd.GetJsonName()) } - if f.Base.L0.ParentFile.Syntax() == protoreflect.Editions { - f.L1.EditionFeatures = mergeEditionFeatures(parent, fd.GetOptions().GetFeatures()) - - if f.L1.EditionFeatures.IsLegacyRequired { - f.L1.Cardinality = protoreflect.Required - } - // We reuse the existing field because the old option `[packed = - // true]` is mutually exclusive with the editions feature. - if canBePacked(fd) { - f.L1.HasPacked = true - f.L1.IsPacked = f.L1.EditionFeatures.IsPacked - } - - // We pretend this option is always explicitly set because the only - // use of HasEnforceUTF8 is to determine whether to use EnforceUTF8 - // or to return the appropriate default. - // When using editions we either parse the option or resolve the - // appropriate default here (instead of later when this option is - // requested from the descriptor). - // In proto2/proto3 syntax HasEnforceUTF8 might be false. - f.L1.HasEnforceUTF8 = true - f.L1.EnforceUTF8 = f.L1.EditionFeatures.IsUTF8Validated + if f.L1.EditionFeatures.IsLegacyRequired { + f.L1.Cardinality = protoreflect.Required + } - if f.L1.Kind == protoreflect.MessageKind && f.L1.EditionFeatures.IsDelimitedEncoded { - f.L1.Kind = protoreflect.GroupKind - } + if f.L1.Kind == protoreflect.MessageKind && f.L1.EditionFeatures.IsDelimitedEncoded { + f.L1.Kind = protoreflect.GroupKind } } return fs, nil @@ -201,12 +181,10 @@ func (r descsByName) initOneofsFromDescriptorProto(ods []*descriptorpb.OneofDesc if o.L0, err = r.makeBase(o, parent, od.GetName(), i, sb); err != nil { return nil, err } + o.L1.EditionFeatures = mergeEditionFeatures(parent, od.GetOptions().GetFeatures()) if opts := od.GetOptions(); opts != nil { opts = proto.Clone(opts).(*descriptorpb.OneofOptions) o.L1.Options = func() protoreflect.ProtoMessage { return opts } - if parent.Syntax() == protoreflect.Editions { - o.L1.EditionFeatures = mergeEditionFeatures(parent, opts.GetFeatures()) - } } } return os, nil @@ -220,10 +198,13 @@ func (r descsByName) initExtensionDeclarations(xds []*descriptorpb.FieldDescript if x.L0, err = r.makeBase(x, parent, xd.GetName(), i, sb); err != nil { return nil, err } + x.L1.EditionFeatures = mergeEditionFeatures(parent, xd.GetOptions().GetFeatures()) if opts := xd.GetOptions(); opts != nil { opts = proto.Clone(opts).(*descriptorpb.FieldOptions) x.L2.Options = func() protoreflect.ProtoMessage { return opts } - x.L2.IsPacked = opts.GetPacked() + if opts.Packed != nil { + x.L1.EditionFeatures.IsPacked = opts.GetPacked() + } } x.L1.Number = protoreflect.FieldNumber(xd.GetNumber()) x.L1.Cardinality = protoreflect.Cardinality(xd.GetLabel()) diff --git a/tools/vendor/google.golang.org/protobuf/reflect/protodesc/desc_resolve.go b/tools/vendor/google.golang.org/protobuf/reflect/protodesc/desc_resolve.go index 254ca58542..f3cebab29c 100644 --- a/tools/vendor/google.golang.org/protobuf/reflect/protodesc/desc_resolve.go +++ b/tools/vendor/google.golang.org/protobuf/reflect/protodesc/desc_resolve.go @@ -46,6 +46,11 @@ func (r *resolver) resolveMessageDependencies(ms []filedesc.Message, mds []*desc if f.L1.Kind, f.L1.Enum, f.L1.Message, err = r.findTarget(f.Kind(), f.Parent().FullName(), partialName(fd.GetTypeName()), f.IsWeak()); err != nil { return errors.New("message field %q cannot resolve type: %v", f.FullName(), err) } + if f.L1.Kind == protoreflect.GroupKind && (f.IsMap() || f.IsMapEntry()) { + // A map field might inherit delimited encoding from a file-wide default feature. + // But maps never actually use delimited encoding. (At least for now...) + f.L1.Kind = protoreflect.MessageKind + } if fd.DefaultValue != nil { v, ev, err := unmarshalDefault(fd.GetDefaultValue(), f, r.allowUnresolvable) if err != nil { diff --git a/tools/vendor/google.golang.org/protobuf/reflect/protodesc/desc_validate.go b/tools/vendor/google.golang.org/protobuf/reflect/protodesc/desc_validate.go index e4dcaf876c..6de31c2ebd 100644 --- a/tools/vendor/google.golang.org/protobuf/reflect/protodesc/desc_validate.go +++ b/tools/vendor/google.golang.org/protobuf/reflect/protodesc/desc_validate.go @@ -45,11 +45,11 @@ func validateEnumDeclarations(es []filedesc.Enum, eds []*descriptorpb.EnumDescri if allowAlias && !foundAlias { return errors.New("enum %q allows aliases, but none were found", e.FullName()) } - if e.Syntax() == protoreflect.Proto3 { + if !e.IsClosed() { if v := e.Values().Get(0); v.Number() != 0 { - return errors.New("enum %q using proto3 semantics must have zero number for the first value", v.FullName()) + return errors.New("enum %q using open semantics must have zero number for the first value", v.FullName()) } - // Verify that value names in proto3 do not conflict if the + // Verify that value names in open enums do not conflict if the // case-insensitive prefix is removed. // See protoc v3.8.0: src/google/protobuf/descriptor.cc:4991-5055 names := map[string]protoreflect.EnumValueDescriptor{} @@ -58,7 +58,7 @@ func validateEnumDeclarations(es []filedesc.Enum, eds []*descriptorpb.EnumDescri v1 := e.Values().Get(i) s := strs.EnumValueName(strs.TrimEnumPrefix(string(v1.Name()), prefix)) if v2, ok := names[s]; ok && v1.Number() != v2.Number() { - return errors.New("enum %q using proto3 semantics has conflict: %q with %q", e.FullName(), v1.Name(), v2.Name()) + return errors.New("enum %q using open semantics has conflict: %q with %q", e.FullName(), v1.Name(), v2.Name()) } names[s] = v1 } @@ -80,7 +80,9 @@ func validateEnumDeclarations(es []filedesc.Enum, eds []*descriptorpb.EnumDescri return nil } -func validateMessageDeclarations(ms []filedesc.Message, mds []*descriptorpb.DescriptorProto) error { +func validateMessageDeclarations(file *filedesc.File, ms []filedesc.Message, mds []*descriptorpb.DescriptorProto) error { + // There are a few limited exceptions only for proto3 + isProto3 := file.L1.Edition == fromEditionProto(descriptorpb.Edition_EDITION_PROTO3) for i, md := range mds { m := &ms[i] @@ -107,25 +109,13 @@ func validateMessageDeclarations(ms []filedesc.Message, mds []*descriptorpb.Desc if isMessageSet && !flags.ProtoLegacy { return errors.New("message %q is a MessageSet, which is a legacy proto1 feature that is no longer supported", m.FullName()) } - if isMessageSet && (m.Syntax() == protoreflect.Proto3 || m.Fields().Len() > 0 || m.ExtensionRanges().Len() == 0) { + if isMessageSet && (isProto3 || m.Fields().Len() > 0 || m.ExtensionRanges().Len() == 0) { return errors.New("message %q is an invalid proto1 MessageSet", m.FullName()) } - if m.Syntax() == protoreflect.Proto3 { + if isProto3 { if m.ExtensionRanges().Len() > 0 { return errors.New("message %q using proto3 semantics cannot have extension ranges", m.FullName()) } - // Verify that field names in proto3 do not conflict if lowercased - // with all underscores removed. - // See protoc v3.8.0: src/google/protobuf/descriptor.cc:5830-5847 - names := map[string]protoreflect.FieldDescriptor{} - for i := 0; i < m.Fields().Len(); i++ { - f1 := m.Fields().Get(i) - s := strings.Replace(strings.ToLower(string(f1.Name())), "_", "", -1) - if f2, ok := names[s]; ok { - return errors.New("message %q using proto3 semantics has conflict: %q with %q", m.FullName(), f1.Name(), f2.Name()) - } - names[s] = f1 - } } for j, fd := range md.GetField() { @@ -149,7 +139,7 @@ func validateMessageDeclarations(ms []filedesc.Message, mds []*descriptorpb.Desc return errors.New("message field %q may not have extendee: %q", f.FullName(), fd.GetExtendee()) } if f.L1.IsProto3Optional { - if f.Syntax() != protoreflect.Proto3 { + if !isProto3 { return errors.New("message field %q under proto3 optional semantics must be specified in the proto3 syntax", f.FullName()) } if f.Cardinality() != protoreflect.Optional { @@ -162,26 +152,29 @@ func validateMessageDeclarations(ms []filedesc.Message, mds []*descriptorpb.Desc if f.IsWeak() && !flags.ProtoLegacy { return errors.New("message field %q is a weak field, which is a legacy proto1 feature that is no longer supported", f.FullName()) } - if f.IsWeak() && (f.Syntax() != protoreflect.Proto2 || !isOptionalMessage(f) || f.ContainingOneof() != nil) { + if f.IsWeak() && (!f.HasPresence() || !isOptionalMessage(f) || f.ContainingOneof() != nil) { return errors.New("message field %q may only be weak for an optional message", f.FullName()) } if f.IsPacked() && !isPackable(f) { return errors.New("message field %q is not packable", f.FullName()) } - if err := checkValidGroup(f); err != nil { + if err := checkValidGroup(file, f); err != nil { return errors.New("message field %q is an invalid group: %v", f.FullName(), err) } if err := checkValidMap(f); err != nil { return errors.New("message field %q is an invalid map: %v", f.FullName(), err) } - if f.Syntax() == protoreflect.Proto3 { + if isProto3 { if f.Cardinality() == protoreflect.Required { return errors.New("message field %q using proto3 semantics cannot be required", f.FullName()) } - if f.Enum() != nil && !f.Enum().IsPlaceholder() && f.Enum().Syntax() != protoreflect.Proto3 { - return errors.New("message field %q using proto3 semantics may only depend on a proto3 enum", f.FullName()) + if f.Enum() != nil && !f.Enum().IsPlaceholder() && f.Enum().IsClosed() { + return errors.New("message field %q using proto3 semantics may only depend on open enums", f.FullName()) } } + if f.Cardinality() == protoreflect.Optional && !f.HasPresence() && f.Enum() != nil && !f.Enum().IsPlaceholder() && f.Enum().IsClosed() { + return errors.New("message field %q with implicit presence may only use open enums", f.FullName()) + } } seenSynthetic := false // synthetic oneofs for proto3 optional must come after real oneofs for j := range md.GetOneofDecl() { @@ -215,17 +208,17 @@ func validateMessageDeclarations(ms []filedesc.Message, mds []*descriptorpb.Desc if err := validateEnumDeclarations(m.L1.Enums.List, md.GetEnumType()); err != nil { return err } - if err := validateMessageDeclarations(m.L1.Messages.List, md.GetNestedType()); err != nil { + if err := validateMessageDeclarations(file, m.L1.Messages.List, md.GetNestedType()); err != nil { return err } - if err := validateExtensionDeclarations(m.L1.Extensions.List, md.GetExtension()); err != nil { + if err := validateExtensionDeclarations(file, m.L1.Extensions.List, md.GetExtension()); err != nil { return err } } return nil } -func validateExtensionDeclarations(xs []filedesc.Extension, xds []*descriptorpb.FieldDescriptorProto) error { +func validateExtensionDeclarations(f *filedesc.File, xs []filedesc.Extension, xds []*descriptorpb.FieldDescriptorProto) error { for i, xd := range xds { x := &xs[i] // NOTE: Avoid using the IsValid method since extensions to MessageSet @@ -267,13 +260,13 @@ func validateExtensionDeclarations(xs []filedesc.Extension, xds []*descriptorpb. if x.IsPacked() && !isPackable(x) { return errors.New("extension field %q is not packable", x.FullName()) } - if err := checkValidGroup(x); err != nil { + if err := checkValidGroup(f, x); err != nil { return errors.New("extension field %q is an invalid group: %v", x.FullName(), err) } if md := x.Message(); md != nil && md.IsMapEntry() { return errors.New("extension field %q cannot be a map entry", x.FullName()) } - if x.Syntax() == protoreflect.Proto3 { + if f.L1.Edition == fromEditionProto(descriptorpb.Edition_EDITION_PROTO3) { switch x.ContainingMessage().FullName() { case (*descriptorpb.FileOptions)(nil).ProtoReflect().Descriptor().FullName(): case (*descriptorpb.EnumOptions)(nil).ProtoReflect().Descriptor().FullName(): @@ -309,21 +302,25 @@ func isPackable(fd protoreflect.FieldDescriptor) bool { // checkValidGroup reports whether fd is a valid group according to the same // rules that protoc imposes. -func checkValidGroup(fd protoreflect.FieldDescriptor) error { +func checkValidGroup(f *filedesc.File, fd protoreflect.FieldDescriptor) error { md := fd.Message() switch { case fd.Kind() != protoreflect.GroupKind: return nil - case fd.Syntax() == protoreflect.Proto3: + case f.L1.Edition == fromEditionProto(descriptorpb.Edition_EDITION_PROTO3): return errors.New("invalid under proto3 semantics") case md == nil || md.IsPlaceholder(): return errors.New("message must be resolvable") - case fd.FullName().Parent() != md.FullName().Parent(): - return errors.New("message and field must be declared in the same scope") - case !unicode.IsUpper(rune(md.Name()[0])): - return errors.New("message name must start with an uppercase") - case fd.Name() != protoreflect.Name(strings.ToLower(string(md.Name()))): - return errors.New("field name must be lowercased form of the message name") + } + if f.L1.Edition < fromEditionProto(descriptorpb.Edition_EDITION_2023) { + switch { + case fd.FullName().Parent() != md.FullName().Parent(): + return errors.New("message and field must be declared in the same scope") + case !unicode.IsUpper(rune(md.Name()[0])): + return errors.New("message name must start with an uppercase") + case fd.Name() != protoreflect.Name(strings.ToLower(string(md.Name()))): + return errors.New("field name must be lowercased form of the message name") + } } return nil } diff --git a/tools/vendor/google.golang.org/protobuf/reflect/protodesc/editions.go b/tools/vendor/google.golang.org/protobuf/reflect/protodesc/editions.go index 2a6b29d179..804830eda3 100644 --- a/tools/vendor/google.golang.org/protobuf/reflect/protodesc/editions.go +++ b/tools/vendor/google.golang.org/protobuf/reflect/protodesc/editions.go @@ -17,11 +17,6 @@ import ( gofeaturespb "google.golang.org/protobuf/types/gofeaturespb" ) -const ( - SupportedEditionsMinimum = descriptorpb.Edition_EDITION_PROTO2 - SupportedEditionsMaximum = descriptorpb.Edition_EDITION_2023 -) - var defaults = &descriptorpb.FeatureSetDefaults{} var defaultsCacheMu sync.Mutex var defaultsCache = make(map[filedesc.Edition]*descriptorpb.FeatureSet) @@ -67,18 +62,20 @@ func getFeatureSetFor(ed filedesc.Edition) *descriptorpb.FeatureSet { fmt.Fprintf(os.Stderr, "internal error: unsupported edition %v (did you forget to update the embedded defaults (i.e. the bootstrap descriptor proto)?)\n", edpb) os.Exit(1) } - fs := defaults.GetDefaults()[0].GetFeatures() + fsed := defaults.GetDefaults()[0] // Using a linear search for now. // Editions are guaranteed to be sorted and thus we could use a binary search. // Given that there are only a handful of editions (with one more per year) // there is not much reason to use a binary search. for _, def := range defaults.GetDefaults() { if def.GetEdition() <= edpb { - fs = def.GetFeatures() + fsed = def } else { break } } + fs := proto.Clone(fsed.GetFixedFeatures()).(*descriptorpb.FeatureSet) + proto.Merge(fs, fsed.GetOverridableFeatures()) defaultsCache[ed] = fs return fs } diff --git a/tools/vendor/google.golang.org/protobuf/reflect/protodesc/proto.go b/tools/vendor/google.golang.org/protobuf/reflect/protodesc/proto.go index 9d6e05420f..a5de8d4001 100644 --- a/tools/vendor/google.golang.org/protobuf/reflect/protodesc/proto.go +++ b/tools/vendor/google.golang.org/protobuf/reflect/protodesc/proto.go @@ -73,6 +73,16 @@ func ToFileDescriptorProto(file protoreflect.FileDescriptor) *descriptorpb.FileD if syntax := file.Syntax(); syntax != protoreflect.Proto2 && syntax.IsValid() { p.Syntax = proto.String(file.Syntax().String()) } + if file.Syntax() == protoreflect.Editions { + desc := file + if fileImportDesc, ok := file.(protoreflect.FileImport); ok { + desc = fileImportDesc.FileDescriptor + } + + if editionsInterface, ok := desc.(interface{ Edition() int32 }); ok { + p.Edition = descriptorpb.Edition(editionsInterface.Edition()).Enum() + } + } return p } @@ -153,6 +163,18 @@ func ToFieldDescriptorProto(field protoreflect.FieldDescriptor) *descriptorpb.Fi if field.Syntax() == protoreflect.Proto3 && field.HasOptionalKeyword() { p.Proto3Optional = proto.Bool(true) } + if field.Syntax() == protoreflect.Editions { + // Editions have no group keyword, this type is only set so that downstream users continue + // treating this as delimited encoding. + if p.GetType() == descriptorpb.FieldDescriptorProto_TYPE_GROUP { + p.Type = descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum() + } + // Editions have no required keyword, this label is only set so that downstream users continue + // treating it as required. + if p.GetLabel() == descriptorpb.FieldDescriptorProto_LABEL_REQUIRED { + p.Label = descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum() + } + } if field.HasDefault() { def, err := defval.Marshal(field.Default(), field.DefaultEnumValue(), field.Kind(), defval.Descriptor) if err != nil && field.DefaultEnumValue() != nil { diff --git a/tools/vendor/google.golang.org/protobuf/reflect/protoreflect/proto.go b/tools/vendor/google.golang.org/protobuf/reflect/protoreflect/proto.go index 00b01fbd8c..c85bfaa5bb 100644 --- a/tools/vendor/google.golang.org/protobuf/reflect/protoreflect/proto.go +++ b/tools/vendor/google.golang.org/protobuf/reflect/protoreflect/proto.go @@ -161,7 +161,7 @@ const ( // IsValid reports whether the syntax is valid. func (s Syntax) IsValid() bool { switch s { - case Proto2, Proto3: + case Proto2, Proto3, Editions: return true default: return false diff --git a/tools/vendor/google.golang.org/protobuf/reflect/protoreflect/source_gen.go b/tools/vendor/google.golang.org/protobuf/reflect/protoreflect/source_gen.go index 7dcc2ff09e..ea154eec44 100644 --- a/tools/vendor/google.golang.org/protobuf/reflect/protoreflect/source_gen.go +++ b/tools/vendor/google.golang.org/protobuf/reflect/protoreflect/source_gen.go @@ -373,6 +373,8 @@ func (p *SourcePath) appendFieldOptions(b []byte) []byte { b = p.appendRepeatedField(b, "edition_defaults", (*SourcePath).appendFieldOptions_EditionDefault) case 21: b = p.appendSingularField(b, "features", (*SourcePath).appendFeatureSet) + case 22: + b = p.appendSingularField(b, "feature_support", (*SourcePath).appendFieldOptions_FeatureSupport) case 999: b = p.appendRepeatedField(b, "uninterpreted_option", (*SourcePath).appendUninterpretedOption) } @@ -483,6 +485,8 @@ func (p *SourcePath) appendEnumValueOptions(b []byte) []byte { b = p.appendSingularField(b, "features", (*SourcePath).appendFeatureSet) case 3: b = p.appendSingularField(b, "debug_redact", nil) + case 4: + b = p.appendSingularField(b, "feature_support", (*SourcePath).appendFieldOptions_FeatureSupport) case 999: b = p.appendRepeatedField(b, "uninterpreted_option", (*SourcePath).appendUninterpretedOption) } @@ -519,6 +523,23 @@ func (p *SourcePath) appendFieldOptions_EditionDefault(b []byte) []byte { return b } +func (p *SourcePath) appendFieldOptions_FeatureSupport(b []byte) []byte { + if len(*p) == 0 { + return b + } + switch (*p)[0] { + case 1: + b = p.appendSingularField(b, "edition_introduced", nil) + case 2: + b = p.appendSingularField(b, "edition_deprecated", nil) + case 3: + b = p.appendSingularField(b, "deprecation_warning", nil) + case 4: + b = p.appendSingularField(b, "edition_removed", nil) + } + return b +} + func (p *SourcePath) appendUninterpretedOption_NamePart(b []byte) []byte { if len(*p) == 0 { return b diff --git a/tools/vendor/google.golang.org/protobuf/reflect/protoreflect/type.go b/tools/vendor/google.golang.org/protobuf/reflect/protoreflect/type.go index 60ff62b4c8..cd8fadbaf8 100644 --- a/tools/vendor/google.golang.org/protobuf/reflect/protoreflect/type.go +++ b/tools/vendor/google.golang.org/protobuf/reflect/protoreflect/type.go @@ -510,7 +510,7 @@ type ExtensionType interface { // // ValueOf is more extensive than protoreflect.ValueOf for a given field's // value as it has more type information available. - ValueOf(interface{}) Value + ValueOf(any) Value // InterfaceOf completely unwraps the Value to the underlying Go type. // InterfaceOf panics if the input is nil or does not represent the @@ -519,13 +519,13 @@ type ExtensionType interface { // // InterfaceOf is able to unwrap the Value further than Value.Interface // as it has more type information available. - InterfaceOf(Value) interface{} + InterfaceOf(Value) any // IsValidValue reports whether the Value is valid to assign to the field. IsValidValue(Value) bool // IsValidInterface reports whether the input is valid to assign to the field. - IsValidInterface(interface{}) bool + IsValidInterface(any) bool } // EnumDescriptor describes an enum and @@ -544,6 +544,12 @@ type EnumDescriptor interface { // ReservedRanges is a list of reserved ranges of enum numbers. ReservedRanges() EnumRanges + // IsClosed reports whether this enum uses closed semantics. + // See https://protobuf.dev/programming-guides/enum/#definitions. + // Note: the Go protobuf implementation is not spec compliant and treats + // all enums as open enums. + IsClosed() bool + isEnumDescriptor } type isEnumDescriptor interface{ ProtoType(EnumDescriptor) } diff --git a/tools/vendor/google.golang.org/protobuf/reflect/protoreflect/value_pure.go b/tools/vendor/google.golang.org/protobuf/reflect/protoreflect/value_pure.go index 7ced876f4e..75f83a2af0 100644 --- a/tools/vendor/google.golang.org/protobuf/reflect/protoreflect/value_pure.go +++ b/tools/vendor/google.golang.org/protobuf/reflect/protoreflect/value_pure.go @@ -32,11 +32,11 @@ const ( type value struct { pragma.DoNotCompare // 0B - typ valueType // 8B - num uint64 // 8B - str string // 16B - bin []byte // 24B - iface interface{} // 16B + typ valueType // 8B + num uint64 // 8B + str string // 16B + bin []byte // 24B + iface any // 16B } func valueOfString(v string) Value { @@ -45,7 +45,7 @@ func valueOfString(v string) Value { func valueOfBytes(v []byte) Value { return Value{typ: bytesType, bin: v} } -func valueOfIface(v interface{}) Value { +func valueOfIface(v any) Value { return Value{typ: ifaceType, iface: v} } @@ -55,6 +55,6 @@ func (v Value) getString() string { func (v Value) getBytes() []byte { return v.bin } -func (v Value) getIface() interface{} { +func (v Value) getIface() any { return v.iface } diff --git a/tools/vendor/google.golang.org/protobuf/reflect/protoreflect/value_union.go b/tools/vendor/google.golang.org/protobuf/reflect/protoreflect/value_union.go index 1603097311..9fe83cef5a 100644 --- a/tools/vendor/google.golang.org/protobuf/reflect/protoreflect/value_union.go +++ b/tools/vendor/google.golang.org/protobuf/reflect/protoreflect/value_union.go @@ -69,8 +69,8 @@ import ( // composite Value. Modifying an empty, read-only value panics. type Value value -// The protoreflect API uses a custom Value union type instead of interface{} -// to keep the future open for performance optimizations. Using an interface{} +// The protoreflect API uses a custom Value union type instead of any +// to keep the future open for performance optimizations. Using an any // always incurs an allocation for primitives (e.g., int64) since it needs to // be boxed on the heap (as interfaces can only contain pointers natively). // Instead, we represent the Value union as a flat struct that internally keeps @@ -85,7 +85,7 @@ type Value value // ValueOf returns a Value initialized with the concrete value stored in v. // This panics if the type does not match one of the allowed types in the // Value union. -func ValueOf(v interface{}) Value { +func ValueOf(v any) Value { switch v := v.(type) { case nil: return Value{} @@ -192,10 +192,10 @@ func (v Value) IsValid() bool { return v.typ != nilType } -// Interface returns v as an interface{}. +// Interface returns v as an any. // // Invariant: v == ValueOf(v).Interface() -func (v Value) Interface() interface{} { +func (v Value) Interface() any { switch v.typ { case nilType: return nil @@ -406,8 +406,8 @@ func (k MapKey) IsValid() bool { return Value(k).IsValid() } -// Interface returns k as an interface{}. -func (k MapKey) Interface() interface{} { +// Interface returns k as an any. +func (k MapKey) Interface() any { return Value(k).Interface() } diff --git a/tools/vendor/google.golang.org/protobuf/reflect/protoreflect/value_unsafe_go120.go b/tools/vendor/google.golang.org/protobuf/reflect/protoreflect/value_unsafe_go120.go index b1fdbe3e8e..7f3583ead8 100644 --- a/tools/vendor/google.golang.org/protobuf/reflect/protoreflect/value_unsafe_go120.go +++ b/tools/vendor/google.golang.org/protobuf/reflect/protoreflect/value_unsafe_go120.go @@ -45,7 +45,7 @@ var ( // typeOf returns a pointer to the Go type information. // The pointer is comparable and equal if and only if the types are identical. -func typeOf(t interface{}) unsafe.Pointer { +func typeOf(t any) unsafe.Pointer { return (*ifaceHeader)(unsafe.Pointer(&t)).Type } @@ -80,7 +80,7 @@ func valueOfBytes(v []byte) Value { p := (*sliceHeader)(unsafe.Pointer(&v)) return Value{typ: bytesType, ptr: p.Data, num: uint64(len(v))} } -func valueOfIface(v interface{}) Value { +func valueOfIface(v any) Value { p := (*ifaceHeader)(unsafe.Pointer(&v)) return Value{typ: p.Type, ptr: p.Data} } @@ -93,7 +93,7 @@ func (v Value) getBytes() (x []byte) { *(*sliceHeader)(unsafe.Pointer(&x)) = sliceHeader{Data: v.ptr, Len: int(v.num), Cap: int(v.num)} return x } -func (v Value) getIface() (x interface{}) { +func (v Value) getIface() (x any) { *(*ifaceHeader)(unsafe.Pointer(&x)) = ifaceHeader{Type: v.typ, Data: v.ptr} return x } diff --git a/tools/vendor/google.golang.org/protobuf/reflect/protoreflect/value_unsafe_go121.go b/tools/vendor/google.golang.org/protobuf/reflect/protoreflect/value_unsafe_go121.go index 4354701117..f7d386990a 100644 --- a/tools/vendor/google.golang.org/protobuf/reflect/protoreflect/value_unsafe_go121.go +++ b/tools/vendor/google.golang.org/protobuf/reflect/protoreflect/value_unsafe_go121.go @@ -15,7 +15,7 @@ import ( type ( ifaceHeader struct { - _ [0]interface{} // if interfaces have greater alignment than unsafe.Pointer, this will enforce it. + _ [0]any // if interfaces have greater alignment than unsafe.Pointer, this will enforce it. Type unsafe.Pointer Data unsafe.Pointer } @@ -37,7 +37,7 @@ var ( // typeOf returns a pointer to the Go type information. // The pointer is comparable and equal if and only if the types are identical. -func typeOf(t interface{}) unsafe.Pointer { +func typeOf(t any) unsafe.Pointer { return (*ifaceHeader)(unsafe.Pointer(&t)).Type } @@ -70,7 +70,7 @@ func valueOfString(v string) Value { func valueOfBytes(v []byte) Value { return Value{typ: bytesType, ptr: unsafe.Pointer(unsafe.SliceData(v)), num: uint64(len(v))} } -func valueOfIface(v interface{}) Value { +func valueOfIface(v any) Value { p := (*ifaceHeader)(unsafe.Pointer(&v)) return Value{typ: p.Type, ptr: p.Data} } @@ -81,7 +81,7 @@ func (v Value) getString() string { func (v Value) getBytes() []byte { return unsafe.Slice((*byte)(v.ptr), v.num) } -func (v Value) getIface() (x interface{}) { +func (v Value) getIface() (x any) { *(*ifaceHeader)(unsafe.Pointer(&x)) = ifaceHeader{Type: v.typ, Data: v.ptr} return x } diff --git a/tools/vendor/google.golang.org/protobuf/reflect/protoregistry/registry.go b/tools/vendor/google.golang.org/protobuf/reflect/protoregistry/registry.go index 6267dc52a6..de17773391 100644 --- a/tools/vendor/google.golang.org/protobuf/reflect/protoregistry/registry.go +++ b/tools/vendor/google.golang.org/protobuf/reflect/protoregistry/registry.go @@ -95,7 +95,7 @@ type Files struct { // multiple files. Only top-level declarations are registered. // Note that enum values are in the top-level since that are in the same // scope as the parent enum. - descsByName map[protoreflect.FullName]interface{} + descsByName map[protoreflect.FullName]any filesByPath map[string][]protoreflect.FileDescriptor numFiles int } @@ -117,7 +117,7 @@ func (r *Files) RegisterFile(file protoreflect.FileDescriptor) error { defer globalMutex.Unlock() } if r.descsByName == nil { - r.descsByName = map[protoreflect.FullName]interface{}{ + r.descsByName = map[protoreflect.FullName]any{ "": &packageDescriptor{}, } r.filesByPath = make(map[string][]protoreflect.FileDescriptor) @@ -485,7 +485,7 @@ type Types struct { } type ( - typesByName map[protoreflect.FullName]interface{} + typesByName map[protoreflect.FullName]any extensionsByMessage map[protoreflect.FullName]extensionsByNumber extensionsByNumber map[protoreflect.FieldNumber]protoreflect.ExtensionType ) @@ -570,7 +570,7 @@ func (r *Types) RegisterExtension(xt protoreflect.ExtensionType) error { return nil } -func (r *Types) register(kind string, desc protoreflect.Descriptor, typ interface{}) error { +func (r *Types) register(kind string, desc protoreflect.Descriptor, typ any) error { name := desc.FullName() prev := r.typesByName[name] if prev != nil { @@ -841,7 +841,7 @@ func (r *Types) RangeExtensionsByMessage(message protoreflect.FullName, f func(p } } -func typeName(t interface{}) string { +func typeName(t any) string { switch t.(type) { case protoreflect.EnumType: return "enum" @@ -854,7 +854,7 @@ func typeName(t interface{}) string { } } -func amendErrorWithCaller(err error, prev, curr interface{}) error { +func amendErrorWithCaller(err error, prev, curr any) error { prevPkg := goPackage(prev) currPkg := goPackage(curr) if prevPkg == "" || currPkg == "" || prevPkg == currPkg { @@ -863,7 +863,7 @@ func amendErrorWithCaller(err error, prev, curr interface{}) error { return errors.New("%s\n\tpreviously from: %q\n\tcurrently from: %q", err, prevPkg, currPkg) } -func goPackage(v interface{}) string { +func goPackage(v any) string { switch d := v.(type) { case protoreflect.EnumType: v = d.Descriptor() diff --git a/tools/vendor/google.golang.org/protobuf/types/descriptorpb/descriptor.pb.go b/tools/vendor/google.golang.org/protobuf/types/descriptorpb/descriptor.pb.go index 78624cf60b..9403eb0750 100644 --- a/tools/vendor/google.golang.org/protobuf/types/descriptorpb/descriptor.pb.go +++ b/tools/vendor/google.golang.org/protobuf/types/descriptorpb/descriptor.pb.go @@ -54,6 +54,9 @@ type Edition int32 const ( // A placeholder for an unknown edition value. Edition_EDITION_UNKNOWN Edition = 0 + // A placeholder edition for specifying default behaviors *before* a feature + // was first introduced. This is effectively an "infinite past". + Edition_EDITION_LEGACY Edition = 900 // Legacy syntax "editions". These pre-date editions, but behave much like // distinct editions. These can't be used to specify the edition of proto // files, but feature definitions must supply proto2/proto3 defaults for @@ -82,6 +85,7 @@ const ( var ( Edition_name = map[int32]string{ 0: "EDITION_UNKNOWN", + 900: "EDITION_LEGACY", 998: "EDITION_PROTO2", 999: "EDITION_PROTO3", 1000: "EDITION_2023", @@ -95,6 +99,7 @@ var ( } Edition_value = map[string]int32{ "EDITION_UNKNOWN": 0, + "EDITION_LEGACY": 900, "EDITION_PROTO2": 998, "EDITION_PROTO3": 999, "EDITION_2023": 1000, @@ -2177,12 +2182,16 @@ type FileOptions struct { // // Deprecated: Marked as deprecated in google/protobuf/descriptor.proto. JavaGenerateEqualsAndHash *bool `protobuf:"varint,20,opt,name=java_generate_equals_and_hash,json=javaGenerateEqualsAndHash" json:"java_generate_equals_and_hash,omitempty"` - // If set true, then the Java2 code generator will generate code that - // throws an exception whenever an attempt is made to assign a non-UTF-8 - // byte sequence to a string field. - // Message reflection will do the same. - // However, an extension field still accepts non-UTF-8 byte sequences. - // This option has no effect on when used with the lite runtime. + // A proto2 file can set this to true to opt in to UTF-8 checking for Java, + // which will throw an exception if invalid UTF-8 is parsed from the wire or + // assigned to a string field. + // + // TODO: clarify exactly what kinds of field types this option + // applies to, and update these docs accordingly. + // + // Proto3 files already perform these checks. Setting the option explicitly to + // false has no effect: it cannot be used to opt proto3 files out of UTF-8 + // checks. JavaStringCheckUtf8 *bool `protobuf:"varint,27,opt,name=java_string_check_utf8,json=javaStringCheckUtf8,def=0" json:"java_string_check_utf8,omitempty"` OptimizeFor *FileOptions_OptimizeMode `protobuf:"varint,9,opt,name=optimize_for,json=optimizeFor,enum=google.protobuf.FileOptions_OptimizeMode,def=1" json:"optimize_for,omitempty"` // Sets the Go package where structs generated from this .proto will be @@ -2679,7 +2688,8 @@ type FieldOptions struct { Targets []FieldOptions_OptionTargetType `protobuf:"varint,19,rep,name=targets,enum=google.protobuf.FieldOptions_OptionTargetType" json:"targets,omitempty"` EditionDefaults []*FieldOptions_EditionDefault `protobuf:"bytes,20,rep,name=edition_defaults,json=editionDefaults" json:"edition_defaults,omitempty"` // Any features defined in the specific edition. - Features *FeatureSet `protobuf:"bytes,21,opt,name=features" json:"features,omitempty"` + Features *FeatureSet `protobuf:"bytes,21,opt,name=features" json:"features,omitempty"` + FeatureSupport *FieldOptions_FeatureSupport `protobuf:"bytes,22,opt,name=feature_support,json=featureSupport" json:"feature_support,omitempty"` // The parser stores options it doesn't recognize here. See above. UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` } @@ -2811,6 +2821,13 @@ func (x *FieldOptions) GetFeatures() *FeatureSet { return nil } +func (x *FieldOptions) GetFeatureSupport() *FieldOptions_FeatureSupport { + if x != nil { + return x.FeatureSupport + } + return nil +} + func (x *FieldOptions) GetUninterpretedOption() []*UninterpretedOption { if x != nil { return x.UninterpretedOption @@ -2995,6 +3012,8 @@ type EnumValueOptions struct { // out when using debug formats, e.g. when the field contains sensitive // credentials. DebugRedact *bool `protobuf:"varint,3,opt,name=debug_redact,json=debugRedact,def=0" json:"debug_redact,omitempty"` + // Information about the support window of a feature value. + FeatureSupport *FieldOptions_FeatureSupport `protobuf:"bytes,4,opt,name=feature_support,json=featureSupport" json:"feature_support,omitempty"` // The parser stores options it doesn't recognize here. See above. UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` } @@ -3058,6 +3077,13 @@ func (x *EnumValueOptions) GetDebugRedact() bool { return Default_EnumValueOptions_DebugRedact } +func (x *EnumValueOptions) GetFeatureSupport() *FieldOptions_FeatureSupport { + if x != nil { + return x.FeatureSupport + } + return nil +} + func (x *EnumValueOptions) GetUninterpretedOption() []*UninterpretedOption { if x != nil { return x.UninterpretedOption @@ -3968,6 +3994,88 @@ func (x *FieldOptions_EditionDefault) GetValue() string { return "" } +// Information about the support window of a feature. +type FieldOptions_FeatureSupport struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The edition that this feature was first available in. In editions + // earlier than this one, the default assigned to EDITION_LEGACY will be + // used, and proto files will not be able to override it. + EditionIntroduced *Edition `protobuf:"varint,1,opt,name=edition_introduced,json=editionIntroduced,enum=google.protobuf.Edition" json:"edition_introduced,omitempty"` + // The edition this feature becomes deprecated in. Using this after this + // edition may trigger warnings. + EditionDeprecated *Edition `protobuf:"varint,2,opt,name=edition_deprecated,json=editionDeprecated,enum=google.protobuf.Edition" json:"edition_deprecated,omitempty"` + // The deprecation warning text if this feature is used after the edition it + // was marked deprecated in. + DeprecationWarning *string `protobuf:"bytes,3,opt,name=deprecation_warning,json=deprecationWarning" json:"deprecation_warning,omitempty"` + // The edition this feature is no longer available in. In editions after + // this one, the last default assigned will be used, and proto files will + // not be able to override it. + EditionRemoved *Edition `protobuf:"varint,4,opt,name=edition_removed,json=editionRemoved,enum=google.protobuf.Edition" json:"edition_removed,omitempty"` +} + +func (x *FieldOptions_FeatureSupport) Reset() { + *x = FieldOptions_FeatureSupport{} + if protoimpl.UnsafeEnabled { + mi := &file_google_protobuf_descriptor_proto_msgTypes[28] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FieldOptions_FeatureSupport) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FieldOptions_FeatureSupport) ProtoMessage() {} + +func (x *FieldOptions_FeatureSupport) ProtoReflect() protoreflect.Message { + mi := &file_google_protobuf_descriptor_proto_msgTypes[28] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FieldOptions_FeatureSupport.ProtoReflect.Descriptor instead. +func (*FieldOptions_FeatureSupport) Descriptor() ([]byte, []int) { + return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{12, 1} +} + +func (x *FieldOptions_FeatureSupport) GetEditionIntroduced() Edition { + if x != nil && x.EditionIntroduced != nil { + return *x.EditionIntroduced + } + return Edition_EDITION_UNKNOWN +} + +func (x *FieldOptions_FeatureSupport) GetEditionDeprecated() Edition { + if x != nil && x.EditionDeprecated != nil { + return *x.EditionDeprecated + } + return Edition_EDITION_UNKNOWN +} + +func (x *FieldOptions_FeatureSupport) GetDeprecationWarning() string { + if x != nil && x.DeprecationWarning != nil { + return *x.DeprecationWarning + } + return "" +} + +func (x *FieldOptions_FeatureSupport) GetEditionRemoved() Edition { + if x != nil && x.EditionRemoved != nil { + return *x.EditionRemoved + } + return Edition_EDITION_UNKNOWN +} + // The name of the uninterpreted option. Each string represents a segment in // a dot-separated name. is_extension is true iff a segment represents an // extension (denoted with parentheses in options specs in .proto files). @@ -3985,7 +4093,7 @@ type UninterpretedOption_NamePart struct { func (x *UninterpretedOption_NamePart) Reset() { *x = UninterpretedOption_NamePart{} if protoimpl.UnsafeEnabled { - mi := &file_google_protobuf_descriptor_proto_msgTypes[28] + mi := &file_google_protobuf_descriptor_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3998,7 +4106,7 @@ func (x *UninterpretedOption_NamePart) String() string { func (*UninterpretedOption_NamePart) ProtoMessage() {} func (x *UninterpretedOption_NamePart) ProtoReflect() protoreflect.Message { - mi := &file_google_protobuf_descriptor_proto_msgTypes[28] + mi := &file_google_protobuf_descriptor_proto_msgTypes[29] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4037,14 +4145,17 @@ type FeatureSetDefaults_FeatureSetEditionDefault struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Edition *Edition `protobuf:"varint,3,opt,name=edition,enum=google.protobuf.Edition" json:"edition,omitempty"` - Features *FeatureSet `protobuf:"bytes,2,opt,name=features" json:"features,omitempty"` + Edition *Edition `protobuf:"varint,3,opt,name=edition,enum=google.protobuf.Edition" json:"edition,omitempty"` + // Defaults of features that can be overridden in this edition. + OverridableFeatures *FeatureSet `protobuf:"bytes,4,opt,name=overridable_features,json=overridableFeatures" json:"overridable_features,omitempty"` + // Defaults of features that can't be overridden in this edition. + FixedFeatures *FeatureSet `protobuf:"bytes,5,opt,name=fixed_features,json=fixedFeatures" json:"fixed_features,omitempty"` } func (x *FeatureSetDefaults_FeatureSetEditionDefault) Reset() { *x = FeatureSetDefaults_FeatureSetEditionDefault{} if protoimpl.UnsafeEnabled { - mi := &file_google_protobuf_descriptor_proto_msgTypes[29] + mi := &file_google_protobuf_descriptor_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4057,7 +4168,7 @@ func (x *FeatureSetDefaults_FeatureSetEditionDefault) String() string { func (*FeatureSetDefaults_FeatureSetEditionDefault) ProtoMessage() {} func (x *FeatureSetDefaults_FeatureSetEditionDefault) ProtoReflect() protoreflect.Message { - mi := &file_google_protobuf_descriptor_proto_msgTypes[29] + mi := &file_google_protobuf_descriptor_proto_msgTypes[30] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4080,9 +4191,16 @@ func (x *FeatureSetDefaults_FeatureSetEditionDefault) GetEdition() Edition { return Edition_EDITION_UNKNOWN } -func (x *FeatureSetDefaults_FeatureSetEditionDefault) GetFeatures() *FeatureSet { +func (x *FeatureSetDefaults_FeatureSetEditionDefault) GetOverridableFeatures() *FeatureSet { if x != nil { - return x.Features + return x.OverridableFeatures + } + return nil +} + +func (x *FeatureSetDefaults_FeatureSetEditionDefault) GetFixedFeatures() *FeatureSet { + if x != nil { + return x.FixedFeatures } return nil } @@ -4188,7 +4306,7 @@ type SourceCodeInfo_Location struct { func (x *SourceCodeInfo_Location) Reset() { *x = SourceCodeInfo_Location{} if protoimpl.UnsafeEnabled { - mi := &file_google_protobuf_descriptor_proto_msgTypes[30] + mi := &file_google_protobuf_descriptor_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4201,7 +4319,7 @@ func (x *SourceCodeInfo_Location) String() string { func (*SourceCodeInfo_Location) ProtoMessage() {} func (x *SourceCodeInfo_Location) ProtoReflect() protoreflect.Message { - mi := &file_google_protobuf_descriptor_proto_msgTypes[30] + mi := &file_google_protobuf_descriptor_proto_msgTypes[31] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4275,7 +4393,7 @@ type GeneratedCodeInfo_Annotation struct { func (x *GeneratedCodeInfo_Annotation) Reset() { *x = GeneratedCodeInfo_Annotation{} if protoimpl.UnsafeEnabled { - mi := &file_google_protobuf_descriptor_proto_msgTypes[31] + mi := &file_google_protobuf_descriptor_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4288,7 +4406,7 @@ func (x *GeneratedCodeInfo_Annotation) String() string { func (*GeneratedCodeInfo_Annotation) ProtoMessage() {} func (x *GeneratedCodeInfo_Annotation) ProtoReflect() protoreflect.Message { - mi := &file_google_protobuf_descriptor_proto_msgTypes[31] + mi := &file_google_protobuf_descriptor_proto_msgTypes[32] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4597,7 +4715,7 @@ var file_google_protobuf_descriptor_proto_rawDesc = []byte{ 0x67, 0x12, 0x30, 0x0a, 0x10, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x0f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x69, 0x6e, 0x67, 0x22, 0x97, 0x09, 0x0a, 0x0b, 0x46, 0x69, 0x6c, 0x65, 0x4f, 0x70, 0x74, 0x69, + 0x69, 0x6e, 0x67, 0x22, 0xad, 0x09, 0x0a, 0x0b, 0x46, 0x69, 0x6c, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x6a, 0x61, 0x76, 0x61, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6a, 0x61, 0x76, 0x61, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x6a, 0x61, 0x76, 0x61, 0x5f, 0x6f, @@ -4670,405 +4788,445 @@ var file_google_protobuf_descriptor_proto_rawDesc = []byte{ 0x45, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x43, 0x4f, 0x44, 0x45, 0x5f, 0x53, 0x49, 0x5a, 0x45, 0x10, 0x02, 0x12, 0x10, 0x0a, 0x0c, 0x4c, 0x49, 0x54, 0x45, 0x5f, 0x52, 0x55, 0x4e, 0x54, 0x49, 0x4d, 0x45, 0x10, 0x03, 0x2a, 0x09, 0x08, 0xe8, 0x07, 0x10, 0x80, 0x80, 0x80, 0x80, - 0x02, 0x4a, 0x04, 0x08, 0x2a, 0x10, 0x2b, 0x4a, 0x04, 0x08, 0x26, 0x10, 0x27, 0x22, 0xf4, 0x03, - 0x0a, 0x0e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x12, 0x3c, 0x0a, 0x17, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x73, 0x65, 0x74, 0x5f, - 0x77, 0x69, 0x72, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x14, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x53, 0x65, 0x74, 0x57, 0x69, 0x72, 0x65, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x4c, - 0x0a, 0x1f, 0x6e, 0x6f, 0x5f, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x5f, 0x64, 0x65, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, - 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x1c, - 0x6e, 0x6f, 0x53, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x6f, 0x72, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x12, 0x25, 0x0a, 0x0a, - 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, - 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, - 0x74, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x70, 0x5f, 0x65, 0x6e, 0x74, 0x72, 0x79, - 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x12, 0x56, 0x0a, 0x26, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x6c, - 0x65, 0x67, 0x61, 0x63, 0x79, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, - 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, - 0x42, 0x02, 0x18, 0x01, 0x52, 0x22, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, - 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x4a, 0x73, 0x6f, 0x6e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x43, - 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x73, 0x12, 0x37, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, - 0x75, 0x72, 0x65, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x65, 0x61, - 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, - 0x73, 0x12, 0x58, 0x0a, 0x14, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, - 0x65, 0x64, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xe7, 0x07, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, - 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, - 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x2a, 0x09, 0x08, 0xe8, 0x07, - 0x10, 0x80, 0x80, 0x80, 0x80, 0x02, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x05, - 0x10, 0x06, 0x4a, 0x04, 0x08, 0x06, 0x10, 0x07, 0x4a, 0x04, 0x08, 0x08, 0x10, 0x09, 0x4a, 0x04, - 0x08, 0x09, 0x10, 0x0a, 0x22, 0xad, 0x0a, 0x0a, 0x0c, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x41, 0x0a, 0x05, 0x63, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x23, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x2e, 0x43, 0x54, 0x79, 0x70, 0x65, 0x3a, 0x06, 0x53, 0x54, 0x52, 0x49, 0x4e, - 0x47, 0x52, 0x05, 0x63, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x61, 0x63, 0x6b, - 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, - 0x12, 0x47, 0x0a, 0x06, 0x6a, 0x73, 0x74, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, - 0x4a, 0x53, 0x54, 0x79, 0x70, 0x65, 0x3a, 0x09, 0x4a, 0x53, 0x5f, 0x4e, 0x4f, 0x52, 0x4d, 0x41, - 0x4c, 0x52, 0x06, 0x6a, 0x73, 0x74, 0x79, 0x70, 0x65, 0x12, 0x19, 0x0a, 0x04, 0x6c, 0x61, 0x7a, - 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x04, - 0x6c, 0x61, 0x7a, 0x79, 0x12, 0x2e, 0x0a, 0x0f, 0x75, 0x6e, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, - 0x65, 0x64, 0x5f, 0x6c, 0x61, 0x7a, 0x79, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, - 0x61, 0x6c, 0x73, 0x65, 0x52, 0x0e, 0x75, 0x6e, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, - 0x4c, 0x61, 0x7a, 0x79, 0x12, 0x25, 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, - 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, - 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x19, 0x0a, 0x04, 0x77, - 0x65, 0x61, 0x6b, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, - 0x52, 0x04, 0x77, 0x65, 0x61, 0x6b, 0x12, 0x28, 0x0a, 0x0c, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, - 0x72, 0x65, 0x64, 0x61, 0x63, 0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, - 0x6c, 0x73, 0x65, 0x52, 0x0b, 0x64, 0x65, 0x62, 0x75, 0x67, 0x52, 0x65, 0x64, 0x61, 0x63, 0x74, - 0x12, 0x4b, 0x0a, 0x09, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x11, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x2d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x2e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x09, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x48, 0x0a, - 0x07, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x18, 0x13, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x2e, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4f, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x07, - 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x12, 0x57, 0x0a, 0x10, 0x65, 0x64, 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x14, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x2c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x2e, 0x45, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, - 0x0f, 0x65, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, - 0x12, 0x37, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x15, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x52, - 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x58, 0x0a, 0x14, 0x75, 0x6e, 0x69, - 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x18, 0xe7, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, - 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x1a, 0x5a, 0x0a, 0x0e, 0x45, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, - 0x66, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x32, 0x0a, 0x07, 0x65, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x07, 0x65, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, - 0x2f, 0x0a, 0x05, 0x43, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x54, 0x52, 0x49, - 0x4e, 0x47, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x43, 0x4f, 0x52, 0x44, 0x10, 0x01, 0x12, 0x10, - 0x0a, 0x0c, 0x53, 0x54, 0x52, 0x49, 0x4e, 0x47, 0x5f, 0x50, 0x49, 0x45, 0x43, 0x45, 0x10, 0x02, - 0x22, 0x35, 0x0a, 0x06, 0x4a, 0x53, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0d, 0x0a, 0x09, 0x4a, 0x53, - 0x5f, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4a, 0x53, 0x5f, - 0x53, 0x54, 0x52, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x4a, 0x53, 0x5f, 0x4e, - 0x55, 0x4d, 0x42, 0x45, 0x52, 0x10, 0x02, 0x22, 0x55, 0x0a, 0x0f, 0x4f, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x15, 0x0a, 0x11, 0x52, 0x45, - 0x54, 0x45, 0x4e, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, - 0x00, 0x12, 0x15, 0x0a, 0x11, 0x52, 0x45, 0x54, 0x45, 0x4e, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x52, - 0x55, 0x4e, 0x54, 0x49, 0x4d, 0x45, 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x52, 0x45, 0x54, 0x45, - 0x4e, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x10, 0x02, 0x22, 0x8c, - 0x02, 0x0a, 0x10, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x54, - 0x79, 0x70, 0x65, 0x12, 0x17, 0x0a, 0x13, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x5f, 0x54, 0x59, - 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, - 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x46, 0x49, 0x4c, 0x45, - 0x10, 0x01, 0x12, 0x1f, 0x0a, 0x1b, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x5f, 0x54, 0x59, 0x50, - 0x45, 0x5f, 0x45, 0x58, 0x54, 0x45, 0x4e, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x52, 0x41, 0x4e, 0x47, - 0x45, 0x10, 0x02, 0x12, 0x17, 0x0a, 0x13, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x5f, 0x54, 0x59, - 0x50, 0x45, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x03, 0x12, 0x15, 0x0a, 0x11, - 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x46, 0x49, 0x45, 0x4c, - 0x44, 0x10, 0x04, 0x12, 0x15, 0x0a, 0x11, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x5f, 0x54, 0x59, - 0x50, 0x45, 0x5f, 0x4f, 0x4e, 0x45, 0x4f, 0x46, 0x10, 0x05, 0x12, 0x14, 0x0a, 0x10, 0x54, 0x41, - 0x52, 0x47, 0x45, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x4e, 0x55, 0x4d, 0x10, 0x06, - 0x12, 0x1a, 0x0a, 0x16, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, - 0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x45, 0x4e, 0x54, 0x52, 0x59, 0x10, 0x07, 0x12, 0x17, 0x0a, 0x13, - 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x45, 0x52, 0x56, - 0x49, 0x43, 0x45, 0x10, 0x08, 0x12, 0x16, 0x0a, 0x12, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x5f, - 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x10, 0x09, 0x2a, 0x09, 0x08, - 0xe8, 0x07, 0x10, 0x80, 0x80, 0x80, 0x80, 0x02, 0x4a, 0x04, 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, - 0x08, 0x12, 0x10, 0x13, 0x22, 0xac, 0x01, 0x0a, 0x0c, 0x4f, 0x6e, 0x65, 0x6f, 0x66, 0x4f, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x37, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, - 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, - 0x65, 0x53, 0x65, 0x74, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x58, - 0x0a, 0x14, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x5f, - 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xe7, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x55, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, - 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x2a, 0x09, 0x08, 0xe8, 0x07, 0x10, 0x80, 0x80, - 0x80, 0x80, 0x02, 0x22, 0xd1, 0x02, 0x0a, 0x0b, 0x45, 0x6e, 0x75, 0x6d, 0x4f, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x5f, 0x61, 0x6c, 0x69, - 0x61, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x41, - 0x6c, 0x69, 0x61, 0x73, 0x12, 0x25, 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, - 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, - 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x56, 0x0a, 0x26, 0x64, - 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, - 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x66, - 0x6c, 0x69, 0x63, 0x74, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x42, 0x02, 0x18, 0x01, 0x52, - 0x22, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x4c, 0x65, 0x67, 0x61, 0x63, - 0x79, 0x4a, 0x73, 0x6f, 0x6e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x6c, 0x69, - 0x63, 0x74, 0x73, 0x12, 0x37, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, - 0x65, 0x74, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x58, 0x0a, 0x14, - 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xe7, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, + 0x02, 0x4a, 0x04, 0x08, 0x2a, 0x10, 0x2b, 0x4a, 0x04, 0x08, 0x26, 0x10, 0x27, 0x52, 0x14, 0x70, + 0x68, 0x70, 0x5f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x73, 0x22, 0xf4, 0x03, 0x0a, 0x0e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x4f, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x3c, 0x0a, 0x17, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x5f, 0x73, 0x65, 0x74, 0x5f, 0x77, 0x69, 0x72, 0x65, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x14, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x53, 0x65, 0x74, 0x57, 0x69, 0x72, 0x65, 0x46, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x12, 0x4c, 0x0a, 0x1f, 0x6e, 0x6f, 0x5f, 0x73, 0x74, 0x61, 0x6e, 0x64, + 0x61, 0x72, 0x64, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x5f, 0x61, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, + 0x61, 0x6c, 0x73, 0x65, 0x52, 0x1c, 0x6e, 0x6f, 0x53, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, + 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x6f, 0x72, 0x12, 0x25, 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x0a, 0x64, + 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x61, 0x70, + 0x5f, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6d, 0x61, + 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x56, 0x0a, 0x26, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, + 0x61, 0x74, 0x65, 0x64, 0x5f, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, + 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x73, + 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x42, 0x02, 0x18, 0x01, 0x52, 0x22, 0x64, 0x65, 0x70, 0x72, + 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x4a, 0x73, 0x6f, 0x6e, + 0x46, 0x69, 0x65, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x73, 0x12, 0x37, + 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x52, 0x08, 0x66, + 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x58, 0x0a, 0x14, 0x75, 0x6e, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0xe7, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x13, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, - 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x2a, 0x09, 0x08, 0xe8, 0x07, 0x10, 0x80, 0x80, 0x80, 0x80, - 0x02, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x22, 0x81, 0x02, 0x0a, 0x10, 0x45, 0x6e, 0x75, 0x6d, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x25, 0x0a, 0x0a, - 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, - 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, - 0x74, 0x65, 0x64, 0x12, 0x37, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, - 0x65, 0x74, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x0c, - 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x72, 0x65, 0x64, 0x61, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, + 0x6e, 0x2a, 0x09, 0x08, 0xe8, 0x07, 0x10, 0x80, 0x80, 0x80, 0x80, 0x02, 0x4a, 0x04, 0x08, 0x04, + 0x10, 0x05, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x4a, 0x04, 0x08, 0x06, 0x10, 0x07, 0x4a, 0x04, + 0x08, 0x08, 0x10, 0x09, 0x4a, 0x04, 0x08, 0x09, 0x10, 0x0a, 0x22, 0x9d, 0x0d, 0x0a, 0x0c, 0x46, + 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x41, 0x0a, 0x05, 0x63, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x23, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, + 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x43, 0x54, 0x79, 0x70, 0x65, 0x3a, + 0x06, 0x53, 0x54, 0x52, 0x49, 0x4e, 0x47, 0x52, 0x05, 0x63, 0x74, 0x79, 0x70, 0x65, 0x12, 0x16, + 0x0a, 0x06, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, + 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x12, 0x47, 0x0a, 0x06, 0x6a, 0x73, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4a, 0x53, 0x54, 0x79, 0x70, 0x65, 0x3a, 0x09, 0x4a, 0x53, + 0x5f, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x52, 0x06, 0x6a, 0x73, 0x74, 0x79, 0x70, 0x65, 0x12, + 0x19, 0x0a, 0x04, 0x6c, 0x61, 0x7a, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, + 0x61, 0x6c, 0x73, 0x65, 0x52, 0x04, 0x6c, 0x61, 0x7a, 0x79, 0x12, 0x2e, 0x0a, 0x0f, 0x75, 0x6e, + 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x6c, 0x61, 0x7a, 0x79, 0x18, 0x0f, 0x20, + 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x0e, 0x75, 0x6e, 0x76, 0x65, + 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x4c, 0x61, 0x7a, 0x79, 0x12, 0x25, 0x0a, 0x0a, 0x64, 0x65, + 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, + 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, + 0x64, 0x12, 0x19, 0x0a, 0x04, 0x77, 0x65, 0x61, 0x6b, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x3a, + 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x04, 0x77, 0x65, 0x61, 0x6b, 0x12, 0x28, 0x0a, 0x0c, + 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x72, 0x65, 0x64, 0x61, 0x63, 0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x0b, 0x64, 0x65, 0x62, 0x75, 0x67, - 0x52, 0x65, 0x64, 0x61, 0x63, 0x74, 0x12, 0x58, 0x0a, 0x14, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, + 0x52, 0x65, 0x64, 0x61, 0x63, 0x74, 0x12, 0x4b, 0x0a, 0x09, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, + 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x48, 0x0a, 0x07, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x18, 0x13, + 0x20, 0x03, 0x28, 0x0e, 0x32, 0x2e, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x2e, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, + 0x54, 0x79, 0x70, 0x65, 0x52, 0x07, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x12, 0x57, 0x0a, + 0x10, 0x65, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, + 0x73, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x45, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, + 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x0f, 0x65, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, + 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x37, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x73, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x53, 0x65, 0x74, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, + 0x55, 0x0a, 0x0f, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x73, 0x75, 0x70, 0x70, 0x6f, + 0x72, 0x74, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, + 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, + 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x0e, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, + 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x58, 0x0a, 0x14, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xe7, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x2a, 0x09, 0x08, 0xe8, 0x07, 0x10, 0x80, 0x80, 0x80, 0x80, 0x02, 0x22, 0xd5, 0x01, 0x0a, 0x0e, - 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x37, - 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x22, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x52, 0x08, 0x66, - 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, - 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x21, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, - 0x73, 0x65, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x58, - 0x0a, 0x14, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x5f, - 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xe7, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, + 0x1a, 0x5a, 0x0a, 0x0e, 0x45, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x66, 0x61, 0x75, + 0x6c, 0x74, 0x12, 0x32, 0x0a, 0x07, 0x65, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x65, + 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x96, 0x02, 0x0a, + 0x0e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x12, + 0x47, 0x0a, 0x12, 0x65, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x74, 0x72, 0x6f, + 0x64, 0x75, 0x63, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x64, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x11, 0x65, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, + 0x74, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x64, 0x12, 0x47, 0x0a, 0x12, 0x65, 0x64, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x11, + 0x65, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, + 0x64, 0x12, 0x2f, 0x0a, 0x13, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x5f, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, + 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x57, 0x61, 0x72, 0x6e, 0x69, + 0x6e, 0x67, 0x12, 0x41, 0x0a, 0x0f, 0x65, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, + 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x64, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x65, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, + 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x22, 0x2f, 0x0a, 0x05, 0x43, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0a, + 0x0a, 0x06, 0x53, 0x54, 0x52, 0x49, 0x4e, 0x47, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x43, 0x4f, + 0x52, 0x44, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x53, 0x54, 0x52, 0x49, 0x4e, 0x47, 0x5f, 0x50, + 0x49, 0x45, 0x43, 0x45, 0x10, 0x02, 0x22, 0x35, 0x0a, 0x06, 0x4a, 0x53, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x0d, 0x0a, 0x09, 0x4a, 0x53, 0x5f, 0x4e, 0x4f, 0x52, 0x4d, 0x41, 0x4c, 0x10, 0x00, 0x12, + 0x0d, 0x0a, 0x09, 0x4a, 0x53, 0x5f, 0x53, 0x54, 0x52, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x0d, + 0x0a, 0x09, 0x4a, 0x53, 0x5f, 0x4e, 0x55, 0x4d, 0x42, 0x45, 0x52, 0x10, 0x02, 0x22, 0x55, 0x0a, + 0x0f, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x15, 0x0a, 0x11, 0x52, 0x45, 0x54, 0x45, 0x4e, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, + 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x52, 0x45, 0x54, 0x45, 0x4e, + 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x52, 0x55, 0x4e, 0x54, 0x49, 0x4d, 0x45, 0x10, 0x01, 0x12, 0x14, + 0x0a, 0x10, 0x52, 0x45, 0x54, 0x45, 0x4e, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x4f, 0x55, 0x52, + 0x43, 0x45, 0x10, 0x02, 0x22, 0x8c, 0x02, 0x0a, 0x10, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x54, + 0x61, 0x72, 0x67, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x17, 0x0a, 0x13, 0x54, 0x41, 0x52, + 0x47, 0x45, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, + 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x1f, 0x0a, 0x1b, 0x54, 0x41, 0x52, 0x47, + 0x45, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x58, 0x54, 0x45, 0x4e, 0x53, 0x49, 0x4f, + 0x4e, 0x5f, 0x52, 0x41, 0x4e, 0x47, 0x45, 0x10, 0x02, 0x12, 0x17, 0x0a, 0x13, 0x54, 0x41, 0x52, + 0x47, 0x45, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, + 0x10, 0x03, 0x12, 0x15, 0x0a, 0x11, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x46, 0x49, 0x45, 0x4c, 0x44, 0x10, 0x04, 0x12, 0x15, 0x0a, 0x11, 0x54, 0x41, 0x52, + 0x47, 0x45, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4f, 0x4e, 0x45, 0x4f, 0x46, 0x10, 0x05, + 0x12, 0x14, 0x0a, 0x10, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, + 0x45, 0x4e, 0x55, 0x4d, 0x10, 0x06, 0x12, 0x1a, 0x0a, 0x16, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, + 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x45, 0x4e, 0x54, 0x52, 0x59, + 0x10, 0x07, 0x12, 0x17, 0x0a, 0x13, 0x54, 0x41, 0x52, 0x47, 0x45, 0x54, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x49, 0x43, 0x45, 0x10, 0x08, 0x12, 0x16, 0x0a, 0x12, 0x54, + 0x41, 0x52, 0x47, 0x45, 0x54, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, + 0x44, 0x10, 0x09, 0x2a, 0x09, 0x08, 0xe8, 0x07, 0x10, 0x80, 0x80, 0x80, 0x80, 0x02, 0x4a, 0x04, + 0x08, 0x04, 0x10, 0x05, 0x4a, 0x04, 0x08, 0x12, 0x10, 0x13, 0x22, 0xac, 0x01, 0x0a, 0x0c, 0x4f, + 0x6e, 0x65, 0x6f, 0x66, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x37, 0x0a, 0x08, 0x66, + 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x55, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, - 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x2a, 0x09, 0x08, 0xe8, 0x07, 0x10, 0x80, 0x80, - 0x80, 0x80, 0x02, 0x22, 0x99, 0x03, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x25, 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, - 0x74, 0x65, 0x64, 0x18, 0x21, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, - 0x52, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x71, 0x0a, 0x11, - 0x69, 0x64, 0x65, 0x6d, 0x70, 0x6f, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x6c, 0x65, 0x76, 0x65, - 0x6c, 0x18, 0x22, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, - 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x49, 0x64, 0x65, 0x6d, 0x70, 0x6f, 0x74, 0x65, - 0x6e, 0x63, 0x79, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x3a, 0x13, 0x49, 0x44, 0x45, 0x4d, 0x50, 0x4f, - 0x54, 0x45, 0x4e, 0x43, 0x59, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x52, 0x10, 0x69, - 0x64, 0x65, 0x6d, 0x70, 0x6f, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, - 0x37, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x23, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x52, 0x08, - 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x58, 0x0a, 0x14, 0x75, 0x6e, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0xe7, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x75, - 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x22, 0x50, 0x0a, 0x10, 0x49, 0x64, 0x65, 0x6d, 0x70, 0x6f, 0x74, 0x65, 0x6e, 0x63, - 0x79, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x17, 0x0a, 0x13, 0x49, 0x44, 0x45, 0x4d, 0x50, 0x4f, - 0x54, 0x45, 0x4e, 0x43, 0x59, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, - 0x13, 0x0a, 0x0f, 0x4e, 0x4f, 0x5f, 0x53, 0x49, 0x44, 0x45, 0x5f, 0x45, 0x46, 0x46, 0x45, 0x43, - 0x54, 0x53, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x49, 0x44, 0x45, 0x4d, 0x50, 0x4f, 0x54, 0x45, - 0x4e, 0x54, 0x10, 0x02, 0x2a, 0x09, 0x08, 0xe8, 0x07, 0x10, 0x80, 0x80, 0x80, 0x80, 0x02, 0x22, - 0x9a, 0x03, 0x0a, 0x13, 0x55, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, - 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x41, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, - 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4e, 0x61, 0x6d, 0x65, - 0x50, 0x61, 0x72, 0x74, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, - 0x65, 0x5f, 0x69, 0x6e, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x10, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x49, 0x6e, 0x74, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x6e, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, - 0x69, 0x6e, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x10, 0x6e, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x49, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0b, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x73, 0x74, 0x72, 0x69, - 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x61, 0x67, 0x67, 0x72, 0x65, - 0x67, 0x61, 0x74, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0e, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x1a, 0x4a, 0x0a, 0x08, 0x4e, 0x61, 0x6d, 0x65, 0x50, 0x61, 0x72, 0x74, 0x12, 0x1b, 0x0a, 0x09, - 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x70, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x02, 0x28, 0x09, 0x52, - 0x08, 0x6e, 0x61, 0x6d, 0x65, 0x50, 0x61, 0x72, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x73, 0x5f, - 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x02, 0x28, 0x08, 0x52, - 0x0b, 0x69, 0x73, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x8c, 0x0a, 0x0a, - 0x0a, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x12, 0x8b, 0x01, 0x0a, 0x0e, - 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x29, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, - 0x74, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, 0x42, - 0x39, 0x88, 0x01, 0x01, 0x98, 0x01, 0x04, 0x98, 0x01, 0x01, 0xa2, 0x01, 0x0d, 0x12, 0x08, 0x45, - 0x58, 0x50, 0x4c, 0x49, 0x43, 0x49, 0x54, 0x18, 0xe6, 0x07, 0xa2, 0x01, 0x0d, 0x12, 0x08, 0x49, - 0x4d, 0x50, 0x4c, 0x49, 0x43, 0x49, 0x54, 0x18, 0xe7, 0x07, 0xa2, 0x01, 0x0d, 0x12, 0x08, 0x45, - 0x58, 0x50, 0x4c, 0x49, 0x43, 0x49, 0x54, 0x18, 0xe8, 0x07, 0x52, 0x0d, 0x66, 0x69, 0x65, 0x6c, - 0x64, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x66, 0x0a, 0x09, 0x65, 0x6e, 0x75, - 0x6d, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x24, 0x2e, 0x67, + 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x73, 0x12, 0x58, 0x0a, 0x14, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, + 0x72, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xe7, 0x07, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, + 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x75, 0x6e, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x2a, 0x09, + 0x08, 0xe8, 0x07, 0x10, 0x80, 0x80, 0x80, 0x80, 0x02, 0x22, 0xd1, 0x02, 0x0a, 0x0b, 0x45, 0x6e, + 0x75, 0x6d, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x6c, 0x6c, + 0x6f, 0x77, 0x5f, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, + 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x25, 0x0a, 0x0a, 0x64, 0x65, + 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, + 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, + 0x64, 0x12, 0x56, 0x0a, 0x26, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x5f, + 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x5f, 0x66, 0x69, 0x65, 0x6c, + 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x08, 0x42, 0x02, 0x18, 0x01, 0x52, 0x22, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, + 0x64, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x4a, 0x73, 0x6f, 0x6e, 0x46, 0x69, 0x65, 0x6c, 0x64, + 0x43, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x73, 0x12, 0x37, 0x0a, 0x08, 0x66, 0x65, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x65, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x73, 0x12, 0x58, 0x0a, 0x14, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, + 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xe7, 0x07, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, + 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x2a, 0x09, 0x08, 0xe8, + 0x07, 0x10, 0x80, 0x80, 0x80, 0x80, 0x02, 0x4a, 0x04, 0x08, 0x05, 0x10, 0x06, 0x22, 0xd8, 0x02, + 0x0a, 0x10, 0x45, 0x6e, 0x75, 0x6d, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x12, 0x25, 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x0a, 0x64, + 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x37, 0x0a, 0x08, 0x66, 0x65, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x65, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x73, 0x12, 0x28, 0x0a, 0x0c, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x72, 0x65, 0x64, 0x61, + 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, + 0x0b, 0x64, 0x65, 0x62, 0x75, 0x67, 0x52, 0x65, 0x64, 0x61, 0x63, 0x74, 0x12, 0x55, 0x0a, 0x0f, + 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x4f, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x75, 0x70, 0x70, + 0x6f, 0x72, 0x74, 0x52, 0x0e, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x75, 0x70, 0x70, + 0x6f, 0x72, 0x74, 0x12, 0x58, 0x0a, 0x14, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, + 0x65, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xe7, 0x07, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, + 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x2a, 0x09, 0x08, + 0xe8, 0x07, 0x10, 0x80, 0x80, 0x80, 0x80, 0x02, 0x22, 0xd5, 0x01, 0x0a, 0x0e, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x37, 0x0a, 0x08, 0x66, + 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x22, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, + 0x65, 0x64, 0x18, 0x21, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, + 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x58, 0x0a, 0x14, 0x75, + 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0xe7, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x13, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x2a, 0x09, 0x08, 0xe8, 0x07, 0x10, 0x80, 0x80, 0x80, 0x80, 0x02, + 0x22, 0x99, 0x03, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x12, 0x25, 0x0a, 0x0a, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, + 0x18, 0x21, 0x20, 0x01, 0x28, 0x08, 0x3a, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x52, 0x0a, 0x64, + 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x71, 0x0a, 0x11, 0x69, 0x64, 0x65, + 0x6d, 0x70, 0x6f, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x22, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2f, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x49, 0x64, 0x65, 0x6d, 0x70, 0x6f, 0x74, 0x65, 0x6e, 0x63, 0x79, + 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x3a, 0x13, 0x49, 0x44, 0x45, 0x4d, 0x50, 0x4f, 0x54, 0x45, 0x4e, + 0x43, 0x59, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x52, 0x10, 0x69, 0x64, 0x65, 0x6d, + 0x70, 0x6f, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x37, 0x0a, 0x08, + 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x23, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x52, 0x08, 0x66, 0x65, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x58, 0x0a, 0x14, 0x75, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xe7, 0x07, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, + 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x75, 0x6e, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, + 0x50, 0x0a, 0x10, 0x49, 0x64, 0x65, 0x6d, 0x70, 0x6f, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x4c, 0x65, + 0x76, 0x65, 0x6c, 0x12, 0x17, 0x0a, 0x13, 0x49, 0x44, 0x45, 0x4d, 0x50, 0x4f, 0x54, 0x45, 0x4e, + 0x43, 0x59, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, + 0x4e, 0x4f, 0x5f, 0x53, 0x49, 0x44, 0x45, 0x5f, 0x45, 0x46, 0x46, 0x45, 0x43, 0x54, 0x53, 0x10, + 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x49, 0x44, 0x45, 0x4d, 0x50, 0x4f, 0x54, 0x45, 0x4e, 0x54, 0x10, + 0x02, 0x2a, 0x09, 0x08, 0xe8, 0x07, 0x10, 0x80, 0x80, 0x80, 0x80, 0x02, 0x22, 0x9a, 0x03, 0x0a, + 0x13, 0x55, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x41, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x6e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x65, 0x74, + 0x65, 0x64, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x50, 0x61, 0x72, + 0x74, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x69, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x66, 0x69, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x69, + 0x6e, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, + 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x49, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x12, 0x2c, 0x0a, 0x12, 0x6e, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x69, 0x6e, 0x74, + 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x6e, 0x65, + 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x49, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x21, + 0x0a, 0x0c, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x01, 0x52, 0x0b, 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, + 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x61, + 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x4a, 0x0a, + 0x08, 0x4e, 0x61, 0x6d, 0x65, 0x50, 0x61, 0x72, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x61, 0x6d, + 0x65, 0x5f, 0x70, 0x61, 0x72, 0x74, 0x18, 0x01, 0x20, 0x02, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x61, + 0x6d, 0x65, 0x50, 0x61, 0x72, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x73, 0x5f, 0x65, 0x78, 0x74, + 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x02, 0x28, 0x08, 0x52, 0x0b, 0x69, 0x73, + 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xa7, 0x0a, 0x0a, 0x0a, 0x46, 0x65, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x12, 0x91, 0x01, 0x0a, 0x0e, 0x66, 0x69, 0x65, + 0x6c, 0x64, 0x5f, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x29, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x2e, 0x46, + 0x69, 0x65, 0x6c, 0x64, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, 0x42, 0x3f, 0x88, 0x01, + 0x01, 0x98, 0x01, 0x04, 0x98, 0x01, 0x01, 0xa2, 0x01, 0x0d, 0x12, 0x08, 0x45, 0x58, 0x50, 0x4c, + 0x49, 0x43, 0x49, 0x54, 0x18, 0xe6, 0x07, 0xa2, 0x01, 0x0d, 0x12, 0x08, 0x49, 0x4d, 0x50, 0x4c, + 0x49, 0x43, 0x49, 0x54, 0x18, 0xe7, 0x07, 0xa2, 0x01, 0x0d, 0x12, 0x08, 0x45, 0x58, 0x50, 0x4c, + 0x49, 0x43, 0x49, 0x54, 0x18, 0xe8, 0x07, 0xb2, 0x01, 0x03, 0x08, 0xe8, 0x07, 0x52, 0x0d, 0x66, + 0x69, 0x65, 0x6c, 0x64, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x6c, 0x0a, 0x09, + 0x65, 0x6e, 0x75, 0x6d, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x24, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x2e, 0x45, 0x6e, 0x75, + 0x6d, 0x54, 0x79, 0x70, 0x65, 0x42, 0x29, 0x88, 0x01, 0x01, 0x98, 0x01, 0x06, 0x98, 0x01, 0x01, + 0xa2, 0x01, 0x0b, 0x12, 0x06, 0x43, 0x4c, 0x4f, 0x53, 0x45, 0x44, 0x18, 0xe6, 0x07, 0xa2, 0x01, + 0x09, 0x12, 0x04, 0x4f, 0x50, 0x45, 0x4e, 0x18, 0xe7, 0x07, 0xb2, 0x01, 0x03, 0x08, 0xe8, 0x07, + 0x52, 0x08, 0x65, 0x6e, 0x75, 0x6d, 0x54, 0x79, 0x70, 0x65, 0x12, 0x98, 0x01, 0x0a, 0x17, 0x72, + 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x65, 0x6e, + 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x31, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, - 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x54, 0x79, - 0x70, 0x65, 0x42, 0x23, 0x88, 0x01, 0x01, 0x98, 0x01, 0x06, 0x98, 0x01, 0x01, 0xa2, 0x01, 0x0b, - 0x12, 0x06, 0x43, 0x4c, 0x4f, 0x53, 0x45, 0x44, 0x18, 0xe6, 0x07, 0xa2, 0x01, 0x09, 0x12, 0x04, - 0x4f, 0x50, 0x45, 0x4e, 0x18, 0xe7, 0x07, 0x52, 0x08, 0x65, 0x6e, 0x75, 0x6d, 0x54, 0x79, 0x70, - 0x65, 0x12, 0x92, 0x01, 0x0a, 0x17, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x66, - 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x31, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, - 0x2e, 0x52, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x45, 0x6e, - 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x42, 0x27, 0x88, 0x01, 0x01, 0x98, 0x01, 0x04, 0x98, 0x01, - 0x01, 0xa2, 0x01, 0x0d, 0x12, 0x08, 0x45, 0x58, 0x50, 0x41, 0x4e, 0x44, 0x45, 0x44, 0x18, 0xe6, - 0x07, 0xa2, 0x01, 0x0b, 0x12, 0x06, 0x50, 0x41, 0x43, 0x4b, 0x45, 0x44, 0x18, 0xe7, 0x07, 0x52, - 0x15, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x45, 0x6e, - 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x78, 0x0a, 0x0f, 0x75, 0x74, 0x66, 0x38, 0x5f, 0x76, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x2a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x2e, 0x55, 0x74, 0x66, - 0x38, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x23, 0x88, 0x01, 0x01, - 0x98, 0x01, 0x04, 0x98, 0x01, 0x01, 0xa2, 0x01, 0x09, 0x12, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x18, - 0xe6, 0x07, 0xa2, 0x01, 0x0b, 0x12, 0x06, 0x56, 0x45, 0x52, 0x49, 0x46, 0x59, 0x18, 0xe7, 0x07, - 0x52, 0x0e, 0x75, 0x74, 0x66, 0x38, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x78, 0x0a, 0x10, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x65, 0x6e, 0x63, 0x6f, - 0x64, 0x69, 0x6e, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x65, 0x61, - 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x45, - 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x42, 0x20, 0x88, 0x01, 0x01, 0x98, 0x01, 0x04, 0x98, - 0x01, 0x01, 0xa2, 0x01, 0x14, 0x12, 0x0f, 0x4c, 0x45, 0x4e, 0x47, 0x54, 0x48, 0x5f, 0x50, 0x52, - 0x45, 0x46, 0x49, 0x58, 0x45, 0x44, 0x18, 0xe6, 0x07, 0x52, 0x0f, 0x6d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x7c, 0x0a, 0x0b, 0x6a, 0x73, - 0x6f, 0x6e, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x26, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x2e, 0x4a, 0x73, 0x6f, - 0x6e, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x42, 0x33, 0x88, 0x01, 0x01, 0x98, 0x01, 0x03, 0x98, - 0x01, 0x06, 0x98, 0x01, 0x01, 0xa2, 0x01, 0x17, 0x12, 0x12, 0x4c, 0x45, 0x47, 0x41, 0x43, 0x59, - 0x5f, 0x42, 0x45, 0x53, 0x54, 0x5f, 0x45, 0x46, 0x46, 0x4f, 0x52, 0x54, 0x18, 0xe6, 0x07, 0xa2, - 0x01, 0x0a, 0x12, 0x05, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x18, 0xe7, 0x07, 0x52, 0x0a, 0x6a, 0x73, - 0x6f, 0x6e, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x22, 0x5c, 0x0a, 0x0d, 0x46, 0x69, 0x65, 0x6c, - 0x64, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x16, 0x46, 0x49, 0x45, - 0x4c, 0x44, 0x5f, 0x50, 0x52, 0x45, 0x53, 0x45, 0x4e, 0x43, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, - 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x45, 0x58, 0x50, 0x4c, 0x49, 0x43, 0x49, - 0x54, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x49, 0x4d, 0x50, 0x4c, 0x49, 0x43, 0x49, 0x54, 0x10, - 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x4c, 0x45, 0x47, 0x41, 0x43, 0x59, 0x5f, 0x52, 0x45, 0x51, 0x55, - 0x49, 0x52, 0x45, 0x44, 0x10, 0x03, 0x22, 0x37, 0x0a, 0x08, 0x45, 0x6e, 0x75, 0x6d, 0x54, 0x79, - 0x70, 0x65, 0x12, 0x15, 0x0a, 0x11, 0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, - 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x4f, 0x50, 0x45, - 0x4e, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x4c, 0x4f, 0x53, 0x45, 0x44, 0x10, 0x02, 0x22, - 0x56, 0x0a, 0x15, 0x52, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x46, 0x69, 0x65, 0x6c, 0x64, - 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x23, 0x0a, 0x1f, 0x52, 0x45, 0x50, 0x45, - 0x41, 0x54, 0x45, 0x44, 0x5f, 0x46, 0x49, 0x45, 0x4c, 0x44, 0x5f, 0x45, 0x4e, 0x43, 0x4f, 0x44, - 0x49, 0x4e, 0x47, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0a, 0x0a, - 0x06, 0x50, 0x41, 0x43, 0x4b, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x45, 0x58, 0x50, - 0x41, 0x4e, 0x44, 0x45, 0x44, 0x10, 0x02, 0x22, 0x43, 0x0a, 0x0e, 0x55, 0x74, 0x66, 0x38, 0x56, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x17, 0x55, 0x54, 0x46, - 0x38, 0x5f, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x4b, - 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x56, 0x45, 0x52, 0x49, 0x46, 0x59, - 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x03, 0x22, 0x53, 0x0a, 0x0f, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x12, - 0x1c, 0x0a, 0x18, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x45, 0x4e, 0x43, 0x4f, 0x44, - 0x49, 0x4e, 0x47, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x13, 0x0a, - 0x0f, 0x4c, 0x45, 0x4e, 0x47, 0x54, 0x48, 0x5f, 0x50, 0x52, 0x45, 0x46, 0x49, 0x58, 0x45, 0x44, - 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x44, 0x45, 0x4c, 0x49, 0x4d, 0x49, 0x54, 0x45, 0x44, 0x10, - 0x02, 0x22, 0x48, 0x0a, 0x0a, 0x4a, 0x73, 0x6f, 0x6e, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, - 0x17, 0x0a, 0x13, 0x4a, 0x53, 0x4f, 0x4e, 0x5f, 0x46, 0x4f, 0x52, 0x4d, 0x41, 0x54, 0x5f, 0x55, - 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x4c, 0x4c, 0x4f, - 0x57, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x4c, 0x45, 0x47, 0x41, 0x43, 0x59, 0x5f, 0x42, 0x45, - 0x53, 0x54, 0x5f, 0x45, 0x46, 0x46, 0x4f, 0x52, 0x54, 0x10, 0x02, 0x2a, 0x06, 0x08, 0xe8, 0x07, - 0x10, 0xe9, 0x07, 0x2a, 0x06, 0x08, 0xe9, 0x07, 0x10, 0xea, 0x07, 0x2a, 0x06, 0x08, 0xea, 0x07, - 0x10, 0xeb, 0x07, 0x2a, 0x06, 0x08, 0x8b, 0x4e, 0x10, 0x90, 0x4e, 0x2a, 0x06, 0x08, 0x90, 0x4e, - 0x10, 0x91, 0x4e, 0x4a, 0x06, 0x08, 0xe7, 0x07, 0x10, 0xe8, 0x07, 0x22, 0xfe, 0x02, 0x0a, 0x12, - 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, - 0x74, 0x73, 0x12, 0x58, 0x0a, 0x08, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, - 0x74, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, - 0x65, 0x53, 0x65, 0x74, 0x45, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x66, 0x61, 0x75, - 0x6c, 0x74, 0x52, 0x08, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x41, 0x0a, 0x0f, - 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x65, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x0e, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x45, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x41, 0x0a, 0x0f, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x5f, 0x65, 0x64, 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x64, 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x69, 0x6d, 0x75, 0x6d, 0x45, 0x64, 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x1a, 0x87, 0x01, 0x0a, 0x18, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, - 0x74, 0x45, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x12, - 0x32, 0x0a, 0x07, 0x65, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x18, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x45, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x65, 0x64, 0x69, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x37, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, - 0x65, 0x74, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x22, 0xa7, 0x02, 0x0a, - 0x0e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, - 0x44, 0x0a, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x28, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x49, 0x6e, - 0x66, 0x6f, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x6c, 0x6f, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0xce, 0x01, 0x0a, 0x08, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x03, 0x28, 0x05, - 0x42, 0x02, 0x10, 0x01, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x04, 0x73, 0x70, - 0x61, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x05, 0x42, 0x02, 0x10, 0x01, 0x52, 0x04, 0x73, 0x70, - 0x61, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x6c, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x6f, - 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6c, 0x65, - 0x61, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x2b, 0x0a, - 0x11, 0x74, 0x72, 0x61, 0x69, 0x6c, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, - 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x74, 0x72, 0x61, 0x69, 0x6c, 0x69, - 0x6e, 0x67, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x3a, 0x0a, 0x19, 0x6c, 0x65, - 0x61, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x65, 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, 0x5f, 0x63, - 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x17, 0x6c, - 0x65, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x44, 0x65, 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, 0x43, 0x6f, - 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0xd0, 0x02, 0x0a, 0x11, 0x47, 0x65, 0x6e, 0x65, 0x72, - 0x61, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x4d, 0x0a, 0x0a, - 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x2d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x64, 0x65, - 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x0a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0xeb, 0x01, 0x0a, 0x0a, - 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x04, 0x70, 0x61, - 0x74, 0x68, 0x18, 0x01, 0x20, 0x03, 0x28, 0x05, 0x42, 0x02, 0x10, 0x01, 0x52, 0x04, 0x70, 0x61, - 0x74, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x66, 0x69, 0x6c, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, - 0x69, 0x6c, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x05, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, 0x52, 0x0a, 0x08, 0x73, - 0x65, 0x6d, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x36, 0x2e, + 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x2e, 0x52, 0x65, 0x70, 0x65, 0x61, 0x74, + 0x65, 0x64, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x42, + 0x2d, 0x88, 0x01, 0x01, 0x98, 0x01, 0x04, 0x98, 0x01, 0x01, 0xa2, 0x01, 0x0d, 0x12, 0x08, 0x45, + 0x58, 0x50, 0x41, 0x4e, 0x44, 0x45, 0x44, 0x18, 0xe6, 0x07, 0xa2, 0x01, 0x0b, 0x12, 0x06, 0x50, + 0x41, 0x43, 0x4b, 0x45, 0x44, 0x18, 0xe7, 0x07, 0xb2, 0x01, 0x03, 0x08, 0xe8, 0x07, 0x52, 0x15, + 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x45, 0x6e, 0x63, + 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x7e, 0x0a, 0x0f, 0x75, 0x74, 0x66, 0x38, 0x5f, 0x76, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2a, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x2e, 0x55, 0x74, 0x66, 0x38, + 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x29, 0x88, 0x01, 0x01, 0x98, + 0x01, 0x04, 0x98, 0x01, 0x01, 0xa2, 0x01, 0x09, 0x12, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x18, 0xe6, + 0x07, 0xa2, 0x01, 0x0b, 0x12, 0x06, 0x56, 0x45, 0x52, 0x49, 0x46, 0x59, 0x18, 0xe7, 0x07, 0xb2, + 0x01, 0x03, 0x08, 0xe8, 0x07, 0x52, 0x0e, 0x75, 0x74, 0x66, 0x38, 0x56, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x7e, 0x0a, 0x10, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x5f, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x2b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x2e, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x42, 0x26, 0x88, 0x01, + 0x01, 0x98, 0x01, 0x04, 0x98, 0x01, 0x01, 0xa2, 0x01, 0x14, 0x12, 0x0f, 0x4c, 0x45, 0x4e, 0x47, + 0x54, 0x48, 0x5f, 0x50, 0x52, 0x45, 0x46, 0x49, 0x58, 0x45, 0x44, 0x18, 0xe6, 0x07, 0xb2, 0x01, + 0x03, 0x08, 0xe8, 0x07, 0x52, 0x0f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x45, 0x6e, 0x63, + 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x82, 0x01, 0x0a, 0x0b, 0x6a, 0x73, 0x6f, 0x6e, 0x5f, 0x66, + 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x65, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x2e, 0x4a, 0x73, 0x6f, 0x6e, 0x46, 0x6f, 0x72, + 0x6d, 0x61, 0x74, 0x42, 0x39, 0x88, 0x01, 0x01, 0x98, 0x01, 0x03, 0x98, 0x01, 0x06, 0x98, 0x01, + 0x01, 0xa2, 0x01, 0x17, 0x12, 0x12, 0x4c, 0x45, 0x47, 0x41, 0x43, 0x59, 0x5f, 0x42, 0x45, 0x53, + 0x54, 0x5f, 0x45, 0x46, 0x46, 0x4f, 0x52, 0x54, 0x18, 0xe6, 0x07, 0xa2, 0x01, 0x0a, 0x12, 0x05, + 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x18, 0xe7, 0x07, 0xb2, 0x01, 0x03, 0x08, 0xe8, 0x07, 0x52, 0x0a, + 0x6a, 0x73, 0x6f, 0x6e, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x22, 0x5c, 0x0a, 0x0d, 0x46, 0x69, + 0x65, 0x6c, 0x64, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x16, 0x46, + 0x49, 0x45, 0x4c, 0x44, 0x5f, 0x50, 0x52, 0x45, 0x53, 0x45, 0x4e, 0x43, 0x45, 0x5f, 0x55, 0x4e, + 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x45, 0x58, 0x50, 0x4c, 0x49, + 0x43, 0x49, 0x54, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x49, 0x4d, 0x50, 0x4c, 0x49, 0x43, 0x49, + 0x54, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x4c, 0x45, 0x47, 0x41, 0x43, 0x59, 0x5f, 0x52, 0x45, + 0x51, 0x55, 0x49, 0x52, 0x45, 0x44, 0x10, 0x03, 0x22, 0x37, 0x0a, 0x08, 0x45, 0x6e, 0x75, 0x6d, + 0x54, 0x79, 0x70, 0x65, 0x12, 0x15, 0x0a, 0x11, 0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x54, 0x59, 0x50, + 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x4f, + 0x50, 0x45, 0x4e, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x4c, 0x4f, 0x53, 0x45, 0x44, 0x10, + 0x02, 0x22, 0x56, 0x0a, 0x15, 0x52, 0x65, 0x70, 0x65, 0x61, 0x74, 0x65, 0x64, 0x46, 0x69, 0x65, + 0x6c, 0x64, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x23, 0x0a, 0x1f, 0x52, 0x45, + 0x50, 0x45, 0x41, 0x54, 0x45, 0x44, 0x5f, 0x46, 0x49, 0x45, 0x4c, 0x44, 0x5f, 0x45, 0x4e, 0x43, + 0x4f, 0x44, 0x49, 0x4e, 0x47, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, + 0x0a, 0x0a, 0x06, 0x50, 0x41, 0x43, 0x4b, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x45, + 0x58, 0x50, 0x41, 0x4e, 0x44, 0x45, 0x44, 0x10, 0x02, 0x22, 0x49, 0x0a, 0x0e, 0x55, 0x74, 0x66, + 0x38, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x17, 0x55, + 0x54, 0x46, 0x38, 0x5f, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, + 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x56, 0x45, 0x52, 0x49, + 0x46, 0x59, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x03, 0x22, 0x04, + 0x08, 0x01, 0x10, 0x01, 0x22, 0x53, 0x0a, 0x0f, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x45, + 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x1c, 0x0a, 0x18, 0x4d, 0x45, 0x53, 0x53, 0x41, + 0x47, 0x45, 0x5f, 0x45, 0x4e, 0x43, 0x4f, 0x44, 0x49, 0x4e, 0x47, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, + 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x4c, 0x45, 0x4e, 0x47, 0x54, 0x48, 0x5f, + 0x50, 0x52, 0x45, 0x46, 0x49, 0x58, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x44, 0x45, + 0x4c, 0x49, 0x4d, 0x49, 0x54, 0x45, 0x44, 0x10, 0x02, 0x22, 0x48, 0x0a, 0x0a, 0x4a, 0x73, 0x6f, + 0x6e, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x17, 0x0a, 0x13, 0x4a, 0x53, 0x4f, 0x4e, 0x5f, + 0x46, 0x4f, 0x52, 0x4d, 0x41, 0x54, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, + 0x12, 0x09, 0x0a, 0x05, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x4c, + 0x45, 0x47, 0x41, 0x43, 0x59, 0x5f, 0x42, 0x45, 0x53, 0x54, 0x5f, 0x45, 0x46, 0x46, 0x4f, 0x52, + 0x54, 0x10, 0x02, 0x2a, 0x06, 0x08, 0xe8, 0x07, 0x10, 0x8b, 0x4e, 0x2a, 0x06, 0x08, 0x8b, 0x4e, + 0x10, 0x90, 0x4e, 0x2a, 0x06, 0x08, 0x90, 0x4e, 0x10, 0x91, 0x4e, 0x4a, 0x06, 0x08, 0xe7, 0x07, + 0x10, 0xe8, 0x07, 0x22, 0xef, 0x03, 0x0a, 0x12, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, + 0x65, 0x74, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x58, 0x0a, 0x08, 0x64, 0x65, + 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3c, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, + 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, + 0x73, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x45, 0x64, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x52, 0x08, 0x64, 0x65, 0x66, 0x61, + 0x75, 0x6c, 0x74, 0x73, 0x12, 0x41, 0x0a, 0x0f, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, 0x5f, + 0x65, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, - 0x6f, 0x2e, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x6d, - 0x61, 0x6e, 0x74, 0x69, 0x63, 0x52, 0x08, 0x73, 0x65, 0x6d, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x22, - 0x28, 0x0a, 0x08, 0x53, 0x65, 0x6d, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x12, 0x08, 0x0a, 0x04, 0x4e, - 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x53, 0x45, 0x54, 0x10, 0x01, 0x12, 0x09, - 0x0a, 0x05, 0x41, 0x4c, 0x49, 0x41, 0x53, 0x10, 0x02, 0x2a, 0x92, 0x02, 0x0a, 0x07, 0x45, 0x64, - 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x13, 0x0a, 0x0f, 0x45, 0x44, 0x49, 0x54, 0x49, 0x4f, 0x4e, - 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0e, 0x45, 0x44, - 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x32, 0x10, 0xe6, 0x07, 0x12, - 0x13, 0x0a, 0x0e, 0x45, 0x44, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x52, 0x4f, 0x54, 0x4f, - 0x33, 0x10, 0xe7, 0x07, 0x12, 0x11, 0x0a, 0x0c, 0x45, 0x44, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, - 0x32, 0x30, 0x32, 0x33, 0x10, 0xe8, 0x07, 0x12, 0x11, 0x0a, 0x0c, 0x45, 0x44, 0x49, 0x54, 0x49, - 0x4f, 0x4e, 0x5f, 0x32, 0x30, 0x32, 0x34, 0x10, 0xe9, 0x07, 0x12, 0x17, 0x0a, 0x13, 0x45, 0x44, - 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x31, 0x5f, 0x54, 0x45, 0x53, 0x54, 0x5f, 0x4f, 0x4e, 0x4c, - 0x59, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x45, 0x44, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x32, - 0x5f, 0x54, 0x45, 0x53, 0x54, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x02, 0x12, 0x1d, 0x0a, 0x17, - 0x45, 0x44, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x39, 0x39, 0x39, 0x39, 0x37, 0x5f, 0x54, 0x45, - 0x53, 0x54, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x9d, 0x8d, 0x06, 0x12, 0x1d, 0x0a, 0x17, 0x45, - 0x44, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x39, 0x39, 0x39, 0x39, 0x38, 0x5f, 0x54, 0x45, 0x53, - 0x54, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x9e, 0x8d, 0x06, 0x12, 0x1d, 0x0a, 0x17, 0x45, 0x44, - 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x39, 0x39, 0x39, 0x39, 0x39, 0x5f, 0x54, 0x45, 0x53, 0x54, - 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x9f, 0x8d, 0x06, 0x12, 0x13, 0x0a, 0x0b, 0x45, 0x44, 0x49, - 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4d, 0x41, 0x58, 0x10, 0xff, 0xff, 0xff, 0xff, 0x07, 0x42, 0x7e, - 0x0a, 0x13, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x42, 0x10, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, - 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x48, 0x01, 0x5a, 0x2d, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x64, 0x65, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, 0xf8, 0x01, 0x01, 0xa2, 0x02, 0x03, 0x47, 0x50, - 0x42, 0xaa, 0x02, 0x1a, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x52, 0x65, 0x66, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x45, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x75, 0x6d, + 0x45, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x41, 0x0a, 0x0f, 0x6d, 0x61, 0x78, 0x69, 0x6d, + 0x75, 0x6d, 0x5f, 0x65, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x18, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x45, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x69, + 0x6d, 0x75, 0x6d, 0x45, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0xf8, 0x01, 0x0a, 0x18, 0x46, + 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x45, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x12, 0x32, 0x0a, 0x07, 0x65, 0x64, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x18, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x64, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x07, 0x65, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4e, 0x0a, 0x14, 0x6f, + 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x66, 0x65, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x65, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x52, 0x13, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x61, + 0x62, 0x6c, 0x65, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x42, 0x0a, 0x0e, 0x66, + 0x69, 0x78, 0x65, 0x64, 0x5f, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, + 0x52, 0x0d, 0x66, 0x69, 0x78, 0x65, 0x64, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x4a, + 0x04, 0x08, 0x01, 0x10, 0x02, 0x4a, 0x04, 0x08, 0x02, 0x10, 0x03, 0x52, 0x08, 0x66, 0x65, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x73, 0x22, 0xa7, 0x02, 0x0a, 0x0e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x43, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x44, 0x0a, 0x08, 0x6c, 0x6f, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x4c, 0x6f, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0xce, + 0x01, 0x0a, 0x08, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x04, 0x70, + 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x03, 0x28, 0x05, 0x42, 0x02, 0x10, 0x01, 0x52, 0x04, 0x70, + 0x61, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x04, 0x73, 0x70, 0x61, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x05, 0x42, 0x02, 0x10, 0x01, 0x52, 0x04, 0x73, 0x70, 0x61, 0x6e, 0x12, 0x29, 0x0a, 0x10, 0x6c, + 0x65, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6c, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x43, 0x6f, + 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x2b, 0x0a, 0x11, 0x74, 0x72, 0x61, 0x69, 0x6c, 0x69, + 0x6e, 0x67, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x10, 0x74, 0x72, 0x61, 0x69, 0x6c, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6d, 0x6d, 0x65, + 0x6e, 0x74, 0x73, 0x12, 0x3a, 0x0a, 0x19, 0x6c, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x64, + 0x65, 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, + 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x17, 0x6c, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x44, + 0x65, 0x74, 0x61, 0x63, 0x68, 0x65, 0x64, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, + 0xd0, 0x02, 0x0a, 0x11, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x64, + 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x4d, 0x0a, 0x0a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x47, 0x65, 0x6e, 0x65, + 0x72, 0x61, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x41, 0x6e, + 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0xeb, 0x01, 0x0a, 0x0a, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x05, 0x42, 0x02, 0x10, 0x01, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0a, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x14, 0x0a, 0x05, + 0x62, 0x65, 0x67, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x62, 0x65, 0x67, + 0x69, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x03, 0x65, 0x6e, 0x64, 0x12, 0x52, 0x0a, 0x08, 0x73, 0x65, 0x6d, 0x61, 0x6e, 0x74, 0x69, 0x63, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x36, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, + 0x65, 0x64, 0x43, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x41, 0x6e, 0x6e, 0x6f, 0x74, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x6d, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x52, 0x08, + 0x73, 0x65, 0x6d, 0x61, 0x6e, 0x74, 0x69, 0x63, 0x22, 0x28, 0x0a, 0x08, 0x53, 0x65, 0x6d, 0x61, + 0x6e, 0x74, 0x69, 0x63, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x07, + 0x0a, 0x03, 0x53, 0x45, 0x54, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x4c, 0x49, 0x41, 0x53, + 0x10, 0x02, 0x2a, 0xa7, 0x02, 0x0a, 0x07, 0x45, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x13, + 0x0a, 0x0f, 0x45, 0x44, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, + 0x4e, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0e, 0x45, 0x44, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4c, + 0x45, 0x47, 0x41, 0x43, 0x59, 0x10, 0x84, 0x07, 0x12, 0x13, 0x0a, 0x0e, 0x45, 0x44, 0x49, 0x54, + 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x32, 0x10, 0xe6, 0x07, 0x12, 0x13, 0x0a, + 0x0e, 0x45, 0x44, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x33, 0x10, + 0xe7, 0x07, 0x12, 0x11, 0x0a, 0x0c, 0x45, 0x44, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x32, 0x30, + 0x32, 0x33, 0x10, 0xe8, 0x07, 0x12, 0x11, 0x0a, 0x0c, 0x45, 0x44, 0x49, 0x54, 0x49, 0x4f, 0x4e, + 0x5f, 0x32, 0x30, 0x32, 0x34, 0x10, 0xe9, 0x07, 0x12, 0x17, 0x0a, 0x13, 0x45, 0x44, 0x49, 0x54, + 0x49, 0x4f, 0x4e, 0x5f, 0x31, 0x5f, 0x54, 0x45, 0x53, 0x54, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, + 0x01, 0x12, 0x17, 0x0a, 0x13, 0x45, 0x44, 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x32, 0x5f, 0x54, + 0x45, 0x53, 0x54, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x02, 0x12, 0x1d, 0x0a, 0x17, 0x45, 0x44, + 0x49, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x39, 0x39, 0x39, 0x39, 0x37, 0x5f, 0x54, 0x45, 0x53, 0x54, + 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x9d, 0x8d, 0x06, 0x12, 0x1d, 0x0a, 0x17, 0x45, 0x44, 0x49, + 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x39, 0x39, 0x39, 0x39, 0x38, 0x5f, 0x54, 0x45, 0x53, 0x54, 0x5f, + 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x9e, 0x8d, 0x06, 0x12, 0x1d, 0x0a, 0x17, 0x45, 0x44, 0x49, 0x54, + 0x49, 0x4f, 0x4e, 0x5f, 0x39, 0x39, 0x39, 0x39, 0x39, 0x5f, 0x54, 0x45, 0x53, 0x54, 0x5f, 0x4f, + 0x4e, 0x4c, 0x59, 0x10, 0x9f, 0x8d, 0x06, 0x12, 0x13, 0x0a, 0x0b, 0x45, 0x44, 0x49, 0x54, 0x49, + 0x4f, 0x4e, 0x5f, 0x4d, 0x41, 0x58, 0x10, 0xff, 0xff, 0xff, 0xff, 0x07, 0x42, 0x7e, 0x0a, 0x13, + 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x42, 0x10, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x48, 0x01, 0x5a, 0x2d, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, 0xf8, 0x01, 0x01, 0xa2, 0x02, 0x03, 0x47, 0x50, 0x42, 0xaa, + 0x02, 0x1a, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x52, 0x65, 0x66, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, } var ( @@ -5084,8 +5242,8 @@ func file_google_protobuf_descriptor_proto_rawDescGZIP() []byte { } var file_google_protobuf_descriptor_proto_enumTypes = make([]protoimpl.EnumInfo, 17) -var file_google_protobuf_descriptor_proto_msgTypes = make([]protoimpl.MessageInfo, 32) -var file_google_protobuf_descriptor_proto_goTypes = []interface{}{ +var file_google_protobuf_descriptor_proto_msgTypes = make([]protoimpl.MessageInfo, 33) +var file_google_protobuf_descriptor_proto_goTypes = []any{ (Edition)(0), // 0: google.protobuf.Edition (ExtensionRangeOptions_VerificationState)(0), // 1: google.protobuf.ExtensionRangeOptions.VerificationState (FieldDescriptorProto_Type)(0), // 2: google.protobuf.FieldDescriptorProto.Type @@ -5131,10 +5289,11 @@ var file_google_protobuf_descriptor_proto_goTypes = []interface{}{ (*ExtensionRangeOptions_Declaration)(nil), // 42: google.protobuf.ExtensionRangeOptions.Declaration (*EnumDescriptorProto_EnumReservedRange)(nil), // 43: google.protobuf.EnumDescriptorProto.EnumReservedRange (*FieldOptions_EditionDefault)(nil), // 44: google.protobuf.FieldOptions.EditionDefault - (*UninterpretedOption_NamePart)(nil), // 45: google.protobuf.UninterpretedOption.NamePart - (*FeatureSetDefaults_FeatureSetEditionDefault)(nil), // 46: google.protobuf.FeatureSetDefaults.FeatureSetEditionDefault - (*SourceCodeInfo_Location)(nil), // 47: google.protobuf.SourceCodeInfo.Location - (*GeneratedCodeInfo_Annotation)(nil), // 48: google.protobuf.GeneratedCodeInfo.Annotation + (*FieldOptions_FeatureSupport)(nil), // 45: google.protobuf.FieldOptions.FeatureSupport + (*UninterpretedOption_NamePart)(nil), // 46: google.protobuf.UninterpretedOption.NamePart + (*FeatureSetDefaults_FeatureSetEditionDefault)(nil), // 47: google.protobuf.FeatureSetDefaults.FeatureSetEditionDefault + (*SourceCodeInfo_Location)(nil), // 48: google.protobuf.SourceCodeInfo.Location + (*GeneratedCodeInfo_Annotation)(nil), // 49: google.protobuf.GeneratedCodeInfo.Annotation } var file_google_protobuf_descriptor_proto_depIdxs = []int32{ 18, // 0: google.protobuf.FileDescriptorSet.file:type_name -> google.protobuf.FileDescriptorProto @@ -5179,40 +5338,46 @@ var file_google_protobuf_descriptor_proto_depIdxs = []int32{ 8, // 39: google.protobuf.FieldOptions.targets:type_name -> google.protobuf.FieldOptions.OptionTargetType 44, // 40: google.protobuf.FieldOptions.edition_defaults:type_name -> google.protobuf.FieldOptions.EditionDefault 36, // 41: google.protobuf.FieldOptions.features:type_name -> google.protobuf.FeatureSet - 35, // 42: google.protobuf.FieldOptions.uninterpreted_option:type_name -> google.protobuf.UninterpretedOption - 36, // 43: google.protobuf.OneofOptions.features:type_name -> google.protobuf.FeatureSet - 35, // 44: google.protobuf.OneofOptions.uninterpreted_option:type_name -> google.protobuf.UninterpretedOption - 36, // 45: google.protobuf.EnumOptions.features:type_name -> google.protobuf.FeatureSet - 35, // 46: google.protobuf.EnumOptions.uninterpreted_option:type_name -> google.protobuf.UninterpretedOption - 36, // 47: google.protobuf.EnumValueOptions.features:type_name -> google.protobuf.FeatureSet - 35, // 48: google.protobuf.EnumValueOptions.uninterpreted_option:type_name -> google.protobuf.UninterpretedOption - 36, // 49: google.protobuf.ServiceOptions.features:type_name -> google.protobuf.FeatureSet - 35, // 50: google.protobuf.ServiceOptions.uninterpreted_option:type_name -> google.protobuf.UninterpretedOption - 9, // 51: google.protobuf.MethodOptions.idempotency_level:type_name -> google.protobuf.MethodOptions.IdempotencyLevel - 36, // 52: google.protobuf.MethodOptions.features:type_name -> google.protobuf.FeatureSet - 35, // 53: google.protobuf.MethodOptions.uninterpreted_option:type_name -> google.protobuf.UninterpretedOption - 45, // 54: google.protobuf.UninterpretedOption.name:type_name -> google.protobuf.UninterpretedOption.NamePart - 10, // 55: google.protobuf.FeatureSet.field_presence:type_name -> google.protobuf.FeatureSet.FieldPresence - 11, // 56: google.protobuf.FeatureSet.enum_type:type_name -> google.protobuf.FeatureSet.EnumType - 12, // 57: google.protobuf.FeatureSet.repeated_field_encoding:type_name -> google.protobuf.FeatureSet.RepeatedFieldEncoding - 13, // 58: google.protobuf.FeatureSet.utf8_validation:type_name -> google.protobuf.FeatureSet.Utf8Validation - 14, // 59: google.protobuf.FeatureSet.message_encoding:type_name -> google.protobuf.FeatureSet.MessageEncoding - 15, // 60: google.protobuf.FeatureSet.json_format:type_name -> google.protobuf.FeatureSet.JsonFormat - 46, // 61: google.protobuf.FeatureSetDefaults.defaults:type_name -> google.protobuf.FeatureSetDefaults.FeatureSetEditionDefault - 0, // 62: google.protobuf.FeatureSetDefaults.minimum_edition:type_name -> google.protobuf.Edition - 0, // 63: google.protobuf.FeatureSetDefaults.maximum_edition:type_name -> google.protobuf.Edition - 47, // 64: google.protobuf.SourceCodeInfo.location:type_name -> google.protobuf.SourceCodeInfo.Location - 48, // 65: google.protobuf.GeneratedCodeInfo.annotation:type_name -> google.protobuf.GeneratedCodeInfo.Annotation - 20, // 66: google.protobuf.DescriptorProto.ExtensionRange.options:type_name -> google.protobuf.ExtensionRangeOptions - 0, // 67: google.protobuf.FieldOptions.EditionDefault.edition:type_name -> google.protobuf.Edition - 0, // 68: google.protobuf.FeatureSetDefaults.FeatureSetEditionDefault.edition:type_name -> google.protobuf.Edition - 36, // 69: google.protobuf.FeatureSetDefaults.FeatureSetEditionDefault.features:type_name -> google.protobuf.FeatureSet - 16, // 70: google.protobuf.GeneratedCodeInfo.Annotation.semantic:type_name -> google.protobuf.GeneratedCodeInfo.Annotation.Semantic - 71, // [71:71] is the sub-list for method output_type - 71, // [71:71] is the sub-list for method input_type - 71, // [71:71] is the sub-list for extension type_name - 71, // [71:71] is the sub-list for extension extendee - 0, // [0:71] is the sub-list for field type_name + 45, // 42: google.protobuf.FieldOptions.feature_support:type_name -> google.protobuf.FieldOptions.FeatureSupport + 35, // 43: google.protobuf.FieldOptions.uninterpreted_option:type_name -> google.protobuf.UninterpretedOption + 36, // 44: google.protobuf.OneofOptions.features:type_name -> google.protobuf.FeatureSet + 35, // 45: google.protobuf.OneofOptions.uninterpreted_option:type_name -> google.protobuf.UninterpretedOption + 36, // 46: google.protobuf.EnumOptions.features:type_name -> google.protobuf.FeatureSet + 35, // 47: google.protobuf.EnumOptions.uninterpreted_option:type_name -> google.protobuf.UninterpretedOption + 36, // 48: google.protobuf.EnumValueOptions.features:type_name -> google.protobuf.FeatureSet + 45, // 49: google.protobuf.EnumValueOptions.feature_support:type_name -> google.protobuf.FieldOptions.FeatureSupport + 35, // 50: google.protobuf.EnumValueOptions.uninterpreted_option:type_name -> google.protobuf.UninterpretedOption + 36, // 51: google.protobuf.ServiceOptions.features:type_name -> google.protobuf.FeatureSet + 35, // 52: google.protobuf.ServiceOptions.uninterpreted_option:type_name -> google.protobuf.UninterpretedOption + 9, // 53: google.protobuf.MethodOptions.idempotency_level:type_name -> google.protobuf.MethodOptions.IdempotencyLevel + 36, // 54: google.protobuf.MethodOptions.features:type_name -> google.protobuf.FeatureSet + 35, // 55: google.protobuf.MethodOptions.uninterpreted_option:type_name -> google.protobuf.UninterpretedOption + 46, // 56: google.protobuf.UninterpretedOption.name:type_name -> google.protobuf.UninterpretedOption.NamePart + 10, // 57: google.protobuf.FeatureSet.field_presence:type_name -> google.protobuf.FeatureSet.FieldPresence + 11, // 58: google.protobuf.FeatureSet.enum_type:type_name -> google.protobuf.FeatureSet.EnumType + 12, // 59: google.protobuf.FeatureSet.repeated_field_encoding:type_name -> google.protobuf.FeatureSet.RepeatedFieldEncoding + 13, // 60: google.protobuf.FeatureSet.utf8_validation:type_name -> google.protobuf.FeatureSet.Utf8Validation + 14, // 61: google.protobuf.FeatureSet.message_encoding:type_name -> google.protobuf.FeatureSet.MessageEncoding + 15, // 62: google.protobuf.FeatureSet.json_format:type_name -> google.protobuf.FeatureSet.JsonFormat + 47, // 63: google.protobuf.FeatureSetDefaults.defaults:type_name -> google.protobuf.FeatureSetDefaults.FeatureSetEditionDefault + 0, // 64: google.protobuf.FeatureSetDefaults.minimum_edition:type_name -> google.protobuf.Edition + 0, // 65: google.protobuf.FeatureSetDefaults.maximum_edition:type_name -> google.protobuf.Edition + 48, // 66: google.protobuf.SourceCodeInfo.location:type_name -> google.protobuf.SourceCodeInfo.Location + 49, // 67: google.protobuf.GeneratedCodeInfo.annotation:type_name -> google.protobuf.GeneratedCodeInfo.Annotation + 20, // 68: google.protobuf.DescriptorProto.ExtensionRange.options:type_name -> google.protobuf.ExtensionRangeOptions + 0, // 69: google.protobuf.FieldOptions.EditionDefault.edition:type_name -> google.protobuf.Edition + 0, // 70: google.protobuf.FieldOptions.FeatureSupport.edition_introduced:type_name -> google.protobuf.Edition + 0, // 71: google.protobuf.FieldOptions.FeatureSupport.edition_deprecated:type_name -> google.protobuf.Edition + 0, // 72: google.protobuf.FieldOptions.FeatureSupport.edition_removed:type_name -> google.protobuf.Edition + 0, // 73: google.protobuf.FeatureSetDefaults.FeatureSetEditionDefault.edition:type_name -> google.protobuf.Edition + 36, // 74: google.protobuf.FeatureSetDefaults.FeatureSetEditionDefault.overridable_features:type_name -> google.protobuf.FeatureSet + 36, // 75: google.protobuf.FeatureSetDefaults.FeatureSetEditionDefault.fixed_features:type_name -> google.protobuf.FeatureSet + 16, // 76: google.protobuf.GeneratedCodeInfo.Annotation.semantic:type_name -> google.protobuf.GeneratedCodeInfo.Annotation.Semantic + 77, // [77:77] is the sub-list for method output_type + 77, // [77:77] is the sub-list for method input_type + 77, // [77:77] is the sub-list for extension type_name + 77, // [77:77] is the sub-list for extension extendee + 0, // [0:77] is the sub-list for field type_name } func init() { file_google_protobuf_descriptor_proto_init() } @@ -5221,7 +5386,7 @@ func file_google_protobuf_descriptor_proto_init() { return } if !protoimpl.UnsafeEnabled { - file_google_protobuf_descriptor_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + file_google_protobuf_descriptor_proto_msgTypes[0].Exporter = func(v any, i int) any { switch v := v.(*FileDescriptorSet); i { case 0: return &v.state @@ -5233,7 +5398,7 @@ func file_google_protobuf_descriptor_proto_init() { return nil } } - file_google_protobuf_descriptor_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + file_google_protobuf_descriptor_proto_msgTypes[1].Exporter = func(v any, i int) any { switch v := v.(*FileDescriptorProto); i { case 0: return &v.state @@ -5245,7 +5410,7 @@ func file_google_protobuf_descriptor_proto_init() { return nil } } - file_google_protobuf_descriptor_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + file_google_protobuf_descriptor_proto_msgTypes[2].Exporter = func(v any, i int) any { switch v := v.(*DescriptorProto); i { case 0: return &v.state @@ -5257,7 +5422,7 @@ func file_google_protobuf_descriptor_proto_init() { return nil } } - file_google_protobuf_descriptor_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + file_google_protobuf_descriptor_proto_msgTypes[3].Exporter = func(v any, i int) any { switch v := v.(*ExtensionRangeOptions); i { case 0: return &v.state @@ -5271,7 +5436,7 @@ func file_google_protobuf_descriptor_proto_init() { return nil } } - file_google_protobuf_descriptor_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + file_google_protobuf_descriptor_proto_msgTypes[4].Exporter = func(v any, i int) any { switch v := v.(*FieldDescriptorProto); i { case 0: return &v.state @@ -5283,7 +5448,7 @@ func file_google_protobuf_descriptor_proto_init() { return nil } } - file_google_protobuf_descriptor_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + file_google_protobuf_descriptor_proto_msgTypes[5].Exporter = func(v any, i int) any { switch v := v.(*OneofDescriptorProto); i { case 0: return &v.state @@ -5295,7 +5460,7 @@ func file_google_protobuf_descriptor_proto_init() { return nil } } - file_google_protobuf_descriptor_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + file_google_protobuf_descriptor_proto_msgTypes[6].Exporter = func(v any, i int) any { switch v := v.(*EnumDescriptorProto); i { case 0: return &v.state @@ -5307,7 +5472,7 @@ func file_google_protobuf_descriptor_proto_init() { return nil } } - file_google_protobuf_descriptor_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + file_google_protobuf_descriptor_proto_msgTypes[7].Exporter = func(v any, i int) any { switch v := v.(*EnumValueDescriptorProto); i { case 0: return &v.state @@ -5319,7 +5484,7 @@ func file_google_protobuf_descriptor_proto_init() { return nil } } - file_google_protobuf_descriptor_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + file_google_protobuf_descriptor_proto_msgTypes[8].Exporter = func(v any, i int) any { switch v := v.(*ServiceDescriptorProto); i { case 0: return &v.state @@ -5331,7 +5496,7 @@ func file_google_protobuf_descriptor_proto_init() { return nil } } - file_google_protobuf_descriptor_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + file_google_protobuf_descriptor_proto_msgTypes[9].Exporter = func(v any, i int) any { switch v := v.(*MethodDescriptorProto); i { case 0: return &v.state @@ -5343,7 +5508,7 @@ func file_google_protobuf_descriptor_proto_init() { return nil } } - file_google_protobuf_descriptor_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + file_google_protobuf_descriptor_proto_msgTypes[10].Exporter = func(v any, i int) any { switch v := v.(*FileOptions); i { case 0: return &v.state @@ -5357,7 +5522,7 @@ func file_google_protobuf_descriptor_proto_init() { return nil } } - file_google_protobuf_descriptor_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + file_google_protobuf_descriptor_proto_msgTypes[11].Exporter = func(v any, i int) any { switch v := v.(*MessageOptions); i { case 0: return &v.state @@ -5371,7 +5536,7 @@ func file_google_protobuf_descriptor_proto_init() { return nil } } - file_google_protobuf_descriptor_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + file_google_protobuf_descriptor_proto_msgTypes[12].Exporter = func(v any, i int) any { switch v := v.(*FieldOptions); i { case 0: return &v.state @@ -5385,7 +5550,7 @@ func file_google_protobuf_descriptor_proto_init() { return nil } } - file_google_protobuf_descriptor_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + file_google_protobuf_descriptor_proto_msgTypes[13].Exporter = func(v any, i int) any { switch v := v.(*OneofOptions); i { case 0: return &v.state @@ -5399,7 +5564,7 @@ func file_google_protobuf_descriptor_proto_init() { return nil } } - file_google_protobuf_descriptor_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + file_google_protobuf_descriptor_proto_msgTypes[14].Exporter = func(v any, i int) any { switch v := v.(*EnumOptions); i { case 0: return &v.state @@ -5413,7 +5578,7 @@ func file_google_protobuf_descriptor_proto_init() { return nil } } - file_google_protobuf_descriptor_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + file_google_protobuf_descriptor_proto_msgTypes[15].Exporter = func(v any, i int) any { switch v := v.(*EnumValueOptions); i { case 0: return &v.state @@ -5427,7 +5592,7 @@ func file_google_protobuf_descriptor_proto_init() { return nil } } - file_google_protobuf_descriptor_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + file_google_protobuf_descriptor_proto_msgTypes[16].Exporter = func(v any, i int) any { switch v := v.(*ServiceOptions); i { case 0: return &v.state @@ -5441,7 +5606,7 @@ func file_google_protobuf_descriptor_proto_init() { return nil } } - file_google_protobuf_descriptor_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + file_google_protobuf_descriptor_proto_msgTypes[17].Exporter = func(v any, i int) any { switch v := v.(*MethodOptions); i { case 0: return &v.state @@ -5455,7 +5620,7 @@ func file_google_protobuf_descriptor_proto_init() { return nil } } - file_google_protobuf_descriptor_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + file_google_protobuf_descriptor_proto_msgTypes[18].Exporter = func(v any, i int) any { switch v := v.(*UninterpretedOption); i { case 0: return &v.state @@ -5467,7 +5632,7 @@ func file_google_protobuf_descriptor_proto_init() { return nil } } - file_google_protobuf_descriptor_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + file_google_protobuf_descriptor_proto_msgTypes[19].Exporter = func(v any, i int) any { switch v := v.(*FeatureSet); i { case 0: return &v.state @@ -5481,7 +5646,7 @@ func file_google_protobuf_descriptor_proto_init() { return nil } } - file_google_protobuf_descriptor_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + file_google_protobuf_descriptor_proto_msgTypes[20].Exporter = func(v any, i int) any { switch v := v.(*FeatureSetDefaults); i { case 0: return &v.state @@ -5493,7 +5658,7 @@ func file_google_protobuf_descriptor_proto_init() { return nil } } - file_google_protobuf_descriptor_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + file_google_protobuf_descriptor_proto_msgTypes[21].Exporter = func(v any, i int) any { switch v := v.(*SourceCodeInfo); i { case 0: return &v.state @@ -5505,7 +5670,7 @@ func file_google_protobuf_descriptor_proto_init() { return nil } } - file_google_protobuf_descriptor_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { + file_google_protobuf_descriptor_proto_msgTypes[22].Exporter = func(v any, i int) any { switch v := v.(*GeneratedCodeInfo); i { case 0: return &v.state @@ -5517,7 +5682,7 @@ func file_google_protobuf_descriptor_proto_init() { return nil } } - file_google_protobuf_descriptor_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { + file_google_protobuf_descriptor_proto_msgTypes[23].Exporter = func(v any, i int) any { switch v := v.(*DescriptorProto_ExtensionRange); i { case 0: return &v.state @@ -5529,7 +5694,7 @@ func file_google_protobuf_descriptor_proto_init() { return nil } } - file_google_protobuf_descriptor_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + file_google_protobuf_descriptor_proto_msgTypes[24].Exporter = func(v any, i int) any { switch v := v.(*DescriptorProto_ReservedRange); i { case 0: return &v.state @@ -5541,7 +5706,7 @@ func file_google_protobuf_descriptor_proto_init() { return nil } } - file_google_protobuf_descriptor_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + file_google_protobuf_descriptor_proto_msgTypes[25].Exporter = func(v any, i int) any { switch v := v.(*ExtensionRangeOptions_Declaration); i { case 0: return &v.state @@ -5553,7 +5718,7 @@ func file_google_protobuf_descriptor_proto_init() { return nil } } - file_google_protobuf_descriptor_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { + file_google_protobuf_descriptor_proto_msgTypes[26].Exporter = func(v any, i int) any { switch v := v.(*EnumDescriptorProto_EnumReservedRange); i { case 0: return &v.state @@ -5565,7 +5730,7 @@ func file_google_protobuf_descriptor_proto_init() { return nil } } - file_google_protobuf_descriptor_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { + file_google_protobuf_descriptor_proto_msgTypes[27].Exporter = func(v any, i int) any { switch v := v.(*FieldOptions_EditionDefault); i { case 0: return &v.state @@ -5577,7 +5742,19 @@ func file_google_protobuf_descriptor_proto_init() { return nil } } - file_google_protobuf_descriptor_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { + file_google_protobuf_descriptor_proto_msgTypes[28].Exporter = func(v any, i int) any { + switch v := v.(*FieldOptions_FeatureSupport); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_google_protobuf_descriptor_proto_msgTypes[29].Exporter = func(v any, i int) any { switch v := v.(*UninterpretedOption_NamePart); i { case 0: return &v.state @@ -5589,7 +5766,7 @@ func file_google_protobuf_descriptor_proto_init() { return nil } } - file_google_protobuf_descriptor_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + file_google_protobuf_descriptor_proto_msgTypes[30].Exporter = func(v any, i int) any { switch v := v.(*FeatureSetDefaults_FeatureSetEditionDefault); i { case 0: return &v.state @@ -5601,7 +5778,7 @@ func file_google_protobuf_descriptor_proto_init() { return nil } } - file_google_protobuf_descriptor_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { + file_google_protobuf_descriptor_proto_msgTypes[31].Exporter = func(v any, i int) any { switch v := v.(*SourceCodeInfo_Location); i { case 0: return &v.state @@ -5613,7 +5790,7 @@ func file_google_protobuf_descriptor_proto_init() { return nil } } - file_google_protobuf_descriptor_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { + file_google_protobuf_descriptor_proto_msgTypes[32].Exporter = func(v any, i int) any { switch v := v.(*GeneratedCodeInfo_Annotation); i { case 0: return &v.state @@ -5632,7 +5809,7 @@ func file_google_protobuf_descriptor_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_google_protobuf_descriptor_proto_rawDesc, NumEnums: 17, - NumMessages: 32, + NumMessages: 33, NumExtensions: 0, NumServices: 0, }, diff --git a/tools/vendor/google.golang.org/protobuf/types/gofeaturespb/go_features.pb.go b/tools/vendor/google.golang.org/protobuf/types/gofeaturespb/go_features.pb.go index 25de5ae008..a2ca940c50 100644 --- a/tools/vendor/google.golang.org/protobuf/types/gofeaturespb/go_features.pb.go +++ b/tools/vendor/google.golang.org/protobuf/types/gofeaturespb/go_features.pb.go @@ -6,9 +6,9 @@ // https://developers.google.com/open-source/licenses/bsd // Code generated by protoc-gen-go. DO NOT EDIT. -// source: reflect/protodesc/proto/go_features.proto +// source: google/protobuf/go_features.proto -package proto +package gofeaturespb import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" @@ -30,7 +30,7 @@ type GoFeatures struct { func (x *GoFeatures) Reset() { *x = GoFeatures{} if protoimpl.UnsafeEnabled { - mi := &file_reflect_protodesc_proto_go_features_proto_msgTypes[0] + mi := &file_google_protobuf_go_features_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -43,7 +43,7 @@ func (x *GoFeatures) String() string { func (*GoFeatures) ProtoMessage() {} func (x *GoFeatures) ProtoReflect() protoreflect.Message { - mi := &file_reflect_protodesc_proto_go_features_proto_msgTypes[0] + mi := &file_google_protobuf_go_features_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -56,7 +56,7 @@ func (x *GoFeatures) ProtoReflect() protoreflect.Message { // Deprecated: Use GoFeatures.ProtoReflect.Descriptor instead. func (*GoFeatures) Descriptor() ([]byte, []int) { - return file_reflect_protodesc_proto_go_features_proto_rawDescGZIP(), []int{0} + return file_google_protobuf_go_features_proto_rawDescGZIP(), []int{0} } func (x *GoFeatures) GetLegacyUnmarshalJsonEnum() bool { @@ -66,69 +66,73 @@ func (x *GoFeatures) GetLegacyUnmarshalJsonEnum() bool { return false } -var file_reflect_protodesc_proto_go_features_proto_extTypes = []protoimpl.ExtensionInfo{ +var file_google_protobuf_go_features_proto_extTypes = []protoimpl.ExtensionInfo{ { ExtendedType: (*descriptorpb.FeatureSet)(nil), ExtensionType: (*GoFeatures)(nil), Field: 1002, - Name: "google.protobuf.go", + Name: "pb.go", Tag: "bytes,1002,opt,name=go", - Filename: "reflect/protodesc/proto/go_features.proto", + Filename: "google/protobuf/go_features.proto", }, } // Extension fields to descriptorpb.FeatureSet. var ( - // optional google.protobuf.GoFeatures go = 1002; - E_Go = &file_reflect_protodesc_proto_go_features_proto_extTypes[0] + // optional pb.GoFeatures go = 1002; + E_Go = &file_google_protobuf_go_features_proto_extTypes[0] ) -var File_reflect_protodesc_proto_go_features_proto protoreflect.FileDescriptor - -var file_reflect_protodesc_proto_go_features_proto_rawDesc = []byte{ - 0x0a, 0x29, 0x72, 0x65, 0x66, 0x6c, 0x65, 0x63, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x64, - 0x65, 0x73, 0x63, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x5f, 0x66, 0x65, 0x61, - 0x74, 0x75, 0x72, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x1a, 0x20, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x6a, - 0x0a, 0x0a, 0x47, 0x6f, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0x5c, 0x0a, 0x1a, - 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x5f, 0x75, 0x6e, 0x6d, 0x61, 0x72, 0x73, 0x68, 0x61, 0x6c, - 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x5f, 0x65, 0x6e, 0x75, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, - 0x42, 0x1f, 0x88, 0x01, 0x01, 0x98, 0x01, 0x06, 0xa2, 0x01, 0x09, 0x12, 0x04, 0x74, 0x72, 0x75, - 0x65, 0x18, 0xe6, 0x07, 0xa2, 0x01, 0x0a, 0x12, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x18, 0xe7, - 0x07, 0x52, 0x17, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x55, 0x6e, 0x6d, 0x61, 0x72, 0x73, 0x68, - 0x61, 0x6c, 0x4a, 0x73, 0x6f, 0x6e, 0x45, 0x6e, 0x75, 0x6d, 0x3a, 0x49, 0x0a, 0x02, 0x67, 0x6f, - 0x12, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x18, 0xea, 0x07, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x47, 0x6f, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, - 0x73, 0x52, 0x02, 0x67, 0x6f, 0x42, 0x34, 0x5a, 0x32, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2f, 0x72, 0x65, 0x66, 0x6c, 0x65, 0x63, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x64, 0x65, 0x73, 0x63, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, +var File_google_protobuf_go_features_proto protoreflect.FileDescriptor + +var file_google_protobuf_go_features_proto_rawDesc = []byte{ + 0x0a, 0x21, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2f, 0x67, 0x6f, 0x5f, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x12, 0x02, 0x70, 0x62, 0x1a, 0x20, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xcd, 0x01, 0x0a, 0x0a, 0x47, 0x6f, + 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x12, 0xbe, 0x01, 0x0a, 0x1a, 0x6c, 0x65, 0x67, + 0x61, 0x63, 0x79, 0x5f, 0x75, 0x6e, 0x6d, 0x61, 0x72, 0x73, 0x68, 0x61, 0x6c, 0x5f, 0x6a, 0x73, + 0x6f, 0x6e, 0x5f, 0x65, 0x6e, 0x75, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x42, 0x80, 0x01, + 0x88, 0x01, 0x01, 0x98, 0x01, 0x06, 0x98, 0x01, 0x01, 0xa2, 0x01, 0x09, 0x12, 0x04, 0x74, 0x72, + 0x75, 0x65, 0x18, 0x84, 0x07, 0xa2, 0x01, 0x0a, 0x12, 0x05, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x18, + 0xe7, 0x07, 0xb2, 0x01, 0x5b, 0x08, 0xe8, 0x07, 0x10, 0xe8, 0x07, 0x1a, 0x53, 0x54, 0x68, 0x65, + 0x20, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x20, 0x55, 0x6e, 0x6d, 0x61, 0x72, 0x73, 0x68, 0x61, + 0x6c, 0x4a, 0x53, 0x4f, 0x4e, 0x20, 0x41, 0x50, 0x49, 0x20, 0x69, 0x73, 0x20, 0x64, 0x65, 0x70, + 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x77, 0x69, 0x6c, 0x6c, + 0x20, 0x62, 0x65, 0x20, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x61, + 0x20, 0x66, 0x75, 0x74, 0x75, 0x72, 0x65, 0x20, 0x65, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, + 0x52, 0x17, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x55, 0x6e, 0x6d, 0x61, 0x72, 0x73, 0x68, 0x61, + 0x6c, 0x4a, 0x73, 0x6f, 0x6e, 0x45, 0x6e, 0x75, 0x6d, 0x3a, 0x3c, 0x0a, 0x02, 0x67, 0x6f, 0x12, + 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x53, 0x65, 0x74, 0x18, 0xea, 0x07, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x47, 0x6f, 0x46, 0x65, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x73, 0x52, 0x02, 0x67, 0x6f, 0x42, 0x2f, 0x5a, 0x2d, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x67, 0x6f, 0x66, 0x65, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x70, 0x62, } var ( - file_reflect_protodesc_proto_go_features_proto_rawDescOnce sync.Once - file_reflect_protodesc_proto_go_features_proto_rawDescData = file_reflect_protodesc_proto_go_features_proto_rawDesc + file_google_protobuf_go_features_proto_rawDescOnce sync.Once + file_google_protobuf_go_features_proto_rawDescData = file_google_protobuf_go_features_proto_rawDesc ) -func file_reflect_protodesc_proto_go_features_proto_rawDescGZIP() []byte { - file_reflect_protodesc_proto_go_features_proto_rawDescOnce.Do(func() { - file_reflect_protodesc_proto_go_features_proto_rawDescData = protoimpl.X.CompressGZIP(file_reflect_protodesc_proto_go_features_proto_rawDescData) +func file_google_protobuf_go_features_proto_rawDescGZIP() []byte { + file_google_protobuf_go_features_proto_rawDescOnce.Do(func() { + file_google_protobuf_go_features_proto_rawDescData = protoimpl.X.CompressGZIP(file_google_protobuf_go_features_proto_rawDescData) }) - return file_reflect_protodesc_proto_go_features_proto_rawDescData + return file_google_protobuf_go_features_proto_rawDescData } -var file_reflect_protodesc_proto_go_features_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_reflect_protodesc_proto_go_features_proto_goTypes = []interface{}{ - (*GoFeatures)(nil), // 0: google.protobuf.GoFeatures +var file_google_protobuf_go_features_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_google_protobuf_go_features_proto_goTypes = []any{ + (*GoFeatures)(nil), // 0: pb.GoFeatures (*descriptorpb.FeatureSet)(nil), // 1: google.protobuf.FeatureSet } -var file_reflect_protodesc_proto_go_features_proto_depIdxs = []int32{ - 1, // 0: google.protobuf.go:extendee -> google.protobuf.FeatureSet - 0, // 1: google.protobuf.go:type_name -> google.protobuf.GoFeatures +var file_google_protobuf_go_features_proto_depIdxs = []int32{ + 1, // 0: pb.go:extendee -> google.protobuf.FeatureSet + 0, // 1: pb.go:type_name -> pb.GoFeatures 2, // [2:2] is the sub-list for method output_type 2, // [2:2] is the sub-list for method input_type 1, // [1:2] is the sub-list for extension type_name @@ -136,13 +140,13 @@ var file_reflect_protodesc_proto_go_features_proto_depIdxs = []int32{ 0, // [0:0] is the sub-list for field type_name } -func init() { file_reflect_protodesc_proto_go_features_proto_init() } -func file_reflect_protodesc_proto_go_features_proto_init() { - if File_reflect_protodesc_proto_go_features_proto != nil { +func init() { file_google_protobuf_go_features_proto_init() } +func file_google_protobuf_go_features_proto_init() { + if File_google_protobuf_go_features_proto != nil { return } if !protoimpl.UnsafeEnabled { - file_reflect_protodesc_proto_go_features_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + file_google_protobuf_go_features_proto_msgTypes[0].Exporter = func(v any, i int) any { switch v := v.(*GoFeatures); i { case 0: return &v.state @@ -159,19 +163,19 @@ func file_reflect_protodesc_proto_go_features_proto_init() { out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_reflect_protodesc_proto_go_features_proto_rawDesc, + RawDescriptor: file_google_protobuf_go_features_proto_rawDesc, NumEnums: 0, NumMessages: 1, NumExtensions: 1, NumServices: 0, }, - GoTypes: file_reflect_protodesc_proto_go_features_proto_goTypes, - DependencyIndexes: file_reflect_protodesc_proto_go_features_proto_depIdxs, - MessageInfos: file_reflect_protodesc_proto_go_features_proto_msgTypes, - ExtensionInfos: file_reflect_protodesc_proto_go_features_proto_extTypes, + GoTypes: file_google_protobuf_go_features_proto_goTypes, + DependencyIndexes: file_google_protobuf_go_features_proto_depIdxs, + MessageInfos: file_google_protobuf_go_features_proto_msgTypes, + ExtensionInfos: file_google_protobuf_go_features_proto_extTypes, }.Build() - File_reflect_protodesc_proto_go_features_proto = out.File - file_reflect_protodesc_proto_go_features_proto_rawDesc = nil - file_reflect_protodesc_proto_go_features_proto_goTypes = nil - file_reflect_protodesc_proto_go_features_proto_depIdxs = nil + File_google_protobuf_go_features_proto = out.File + file_google_protobuf_go_features_proto_rawDesc = nil + file_google_protobuf_go_features_proto_goTypes = nil + file_google_protobuf_go_features_proto_depIdxs = nil } diff --git a/tools/vendor/google.golang.org/protobuf/types/gofeaturespb/go_features.proto b/tools/vendor/google.golang.org/protobuf/types/gofeaturespb/go_features.proto deleted file mode 100644 index d246571296..0000000000 --- a/tools/vendor/google.golang.org/protobuf/types/gofeaturespb/go_features.proto +++ /dev/null @@ -1,28 +0,0 @@ -// Protocol Buffers - Google's data interchange format -// Copyright 2023 Google Inc. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file or at -// https://developers.google.com/open-source/licenses/bsd - -syntax = "proto2"; - -package google.protobuf; - -import "google/protobuf/descriptor.proto"; - -option go_package = "google.golang.org/protobuf/types/gofeaturespb"; - -extend google.protobuf.FeatureSet { - optional GoFeatures go = 1002; -} - -message GoFeatures { - // Whether or not to generate the deprecated UnmarshalJSON method for enums. - optional bool legacy_unmarshal_json_enum = 1 [ - retention = RETENTION_RUNTIME, - targets = TARGET_TYPE_ENUM, - edition_defaults = { edition: EDITION_PROTO2, value: "true" }, - edition_defaults = { edition: EDITION_PROTO3, value: "false" } - ]; -} diff --git a/tools/vendor/google.golang.org/protobuf/types/known/anypb/any.pb.go b/tools/vendor/google.golang.org/protobuf/types/known/anypb/any.pb.go index 9de51be540..7172b43d38 100644 --- a/tools/vendor/google.golang.org/protobuf/types/known/anypb/any.pb.go +++ b/tools/vendor/google.golang.org/protobuf/types/known/anypb/any.pb.go @@ -445,7 +445,7 @@ func file_google_protobuf_any_proto_rawDescGZIP() []byte { } var file_google_protobuf_any_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_google_protobuf_any_proto_goTypes = []interface{}{ +var file_google_protobuf_any_proto_goTypes = []any{ (*Any)(nil), // 0: google.protobuf.Any } var file_google_protobuf_any_proto_depIdxs = []int32{ @@ -462,7 +462,7 @@ func file_google_protobuf_any_proto_init() { return } if !protoimpl.UnsafeEnabled { - file_google_protobuf_any_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + file_google_protobuf_any_proto_msgTypes[0].Exporter = func(v any, i int) any { switch v := v.(*Any); i { case 0: return &v.state diff --git a/tools/vendor/google.golang.org/protobuf/types/known/durationpb/duration.pb.go b/tools/vendor/google.golang.org/protobuf/types/known/durationpb/duration.pb.go index df709a8dd4..1b71bcd910 100644 --- a/tools/vendor/google.golang.org/protobuf/types/known/durationpb/duration.pb.go +++ b/tools/vendor/google.golang.org/protobuf/types/known/durationpb/duration.pb.go @@ -323,7 +323,7 @@ func file_google_protobuf_duration_proto_rawDescGZIP() []byte { } var file_google_protobuf_duration_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_google_protobuf_duration_proto_goTypes = []interface{}{ +var file_google_protobuf_duration_proto_goTypes = []any{ (*Duration)(nil), // 0: google.protobuf.Duration } var file_google_protobuf_duration_proto_depIdxs = []int32{ @@ -340,7 +340,7 @@ func file_google_protobuf_duration_proto_init() { return } if !protoimpl.UnsafeEnabled { - file_google_protobuf_duration_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + file_google_protobuf_duration_proto_msgTypes[0].Exporter = func(v any, i int) any { switch v := v.(*Duration); i { case 0: return &v.state diff --git a/tools/vendor/google.golang.org/protobuf/types/known/timestamppb/timestamp.pb.go b/tools/vendor/google.golang.org/protobuf/types/known/timestamppb/timestamp.pb.go index 81511a3363..83a5a645b0 100644 --- a/tools/vendor/google.golang.org/protobuf/types/known/timestamppb/timestamp.pb.go +++ b/tools/vendor/google.golang.org/protobuf/types/known/timestamppb/timestamp.pb.go @@ -332,7 +332,7 @@ func file_google_protobuf_timestamp_proto_rawDescGZIP() []byte { } var file_google_protobuf_timestamp_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_google_protobuf_timestamp_proto_goTypes = []interface{}{ +var file_google_protobuf_timestamp_proto_goTypes = []any{ (*Timestamp)(nil), // 0: google.protobuf.Timestamp } var file_google_protobuf_timestamp_proto_depIdxs = []int32{ @@ -349,7 +349,7 @@ func file_google_protobuf_timestamp_proto_init() { return } if !protoimpl.UnsafeEnabled { - file_google_protobuf_timestamp_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + file_google_protobuf_timestamp_proto_msgTypes[0].Exporter = func(v any, i int) any { switch v := v.(*Timestamp); i { case 0: return &v.state diff --git a/tools/vendor/honnef.co/go/tools/analysis/callcheck/callcheck.go b/tools/vendor/honnef.co/go/tools/analysis/callcheck/callcheck.go new file mode 100644 index 0000000000..11ec3fb91c --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/analysis/callcheck/callcheck.go @@ -0,0 +1,161 @@ +// Package callcheck provides a framework for validating arguments in function calls. +package callcheck + +import ( + "fmt" + "go/ast" + "go/constant" + "go/types" + + "golang.org/x/tools/go/analysis" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/ir" + "honnef.co/go/tools/go/ir/irutil" + "honnef.co/go/tools/go/types/typeutil" + "honnef.co/go/tools/internal/passes/buildir" +) + +type Call struct { + Pass *analysis.Pass + Instr ir.CallInstruction + Args []*Argument + + Parent *ir.Function + + invalids []string +} + +func (c *Call) Invalid(msg string) { + c.invalids = append(c.invalids, msg) +} + +type Argument struct { + Value Value + invalids []string +} + +type Value struct { + Value ir.Value +} + +func (arg *Argument) Invalid(msg string) { + arg.invalids = append(arg.invalids, msg) +} + +type Check func(call *Call) + +func Analyzer(rules map[string]Check) func(pass *analysis.Pass) (interface{}, error) { + return func(pass *analysis.Pass) (interface{}, error) { + return checkCalls(pass, rules) + } +} + +func checkCalls(pass *analysis.Pass, rules map[string]Check) (interface{}, error) { + cb := func(caller *ir.Function, site ir.CallInstruction, callee *ir.Function) { + obj, ok := callee.Object().(*types.Func) + if !ok { + return + } + + r, ok := rules[typeutil.FuncName(obj)] + if !ok { + return + } + var args []*Argument + irargs := site.Common().Args + if callee.Signature.Recv() != nil { + irargs = irargs[1:] + } + for _, arg := range irargs { + if iarg, ok := arg.(*ir.MakeInterface); ok { + arg = iarg.X + } + args = append(args, &Argument{Value: Value{arg}}) + } + call := &Call{ + Pass: pass, + Instr: site, + Args: args, + Parent: site.Parent(), + } + r(call) + + var astcall *ast.CallExpr + switch source := site.Source().(type) { + case *ast.CallExpr: + astcall = source + case *ast.DeferStmt: + astcall = source.Call + case *ast.GoStmt: + astcall = source.Call + case nil: + // TODO(dh): I am not sure this can actually happen. If it + // can't, we should remove this case, and also stop + // checking for astcall == nil in the code that follows. + default: + panic(fmt.Sprintf("unhandled case %T", source)) + } + + for idx, arg := range call.Args { + for _, e := range arg.invalids { + if astcall != nil { + if idx < len(astcall.Args) { + report.Report(pass, astcall.Args[idx], e) + } else { + // this is an instance of fn1(fn2()) where fn2 + // returns multiple values. Report the error + // at the next-best position that we have, the + // first argument. An example of a check that + // triggers this is checkEncodingBinaryRules. + report.Report(pass, astcall.Args[0], e) + } + } else { + report.Report(pass, site, e) + } + } + } + for _, e := range call.invalids { + report.Report(pass, call.Instr, e) + } + } + for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { + eachCall(fn, cb) + } + return nil, nil +} + +func eachCall(fn *ir.Function, cb func(caller *ir.Function, site ir.CallInstruction, callee *ir.Function)) { + for _, b := range fn.Blocks { + for _, instr := range b.Instrs { + if site, ok := instr.(ir.CallInstruction); ok { + if g := site.Common().StaticCallee(); g != nil { + cb(fn, site, g) + } + } + } + } +} + +func ExtractConstExpectKind(v Value, kind constant.Kind) *ir.Const { + k := extractConst(v.Value) + if k == nil || k.Value == nil || k.Value.Kind() != kind { + return nil + } + return k +} + +func ExtractConst(v Value) *ir.Const { + return extractConst(v.Value) +} + +func extractConst(v ir.Value) *ir.Const { + v = irutil.Flatten(v) + switch v := v.(type) { + case *ir.Const: + return v + case *ir.MakeInterface: + return extractConst(v.X) + default: + return nil + } +} diff --git a/tools/vendor/honnef.co/go/tools/analysis/code/code.go b/tools/vendor/honnef.co/go/tools/analysis/code/code.go index f200363d9d..7d20ea4c97 100644 --- a/tools/vendor/honnef.co/go/tools/analysis/code/code.go +++ b/tools/vendor/honnef.co/go/tools/analysis/code/code.go @@ -2,12 +2,14 @@ package code import ( - "flag" "fmt" "go/ast" + "go/build/constraint" "go/constant" "go/token" "go/types" + "go/version" + "path/filepath" "strings" "honnef.co/go/tools/analysis/facts/generated" @@ -15,6 +17,7 @@ import ( "honnef.co/go/tools/analysis/facts/tokenfile" "honnef.co/go/tools/go/ast/astutil" "honnef.co/go/tools/go/types/typeutil" + "honnef.co/go/tools/knowledge" "honnef.co/go/tools/pattern" "golang.org/x/tools/go/analysis" @@ -24,8 +27,30 @@ type Positioner interface { Pos() token.Pos } -func IsOfType(pass *analysis.Pass, expr ast.Expr, name string) bool { - return typeutil.IsType(pass.TypesInfo.TypeOf(expr), name) +func IsOfStringConvertibleByteSlice(pass *analysis.Pass, expr ast.Expr) bool { + typ, ok := pass.TypesInfo.TypeOf(expr).Underlying().(*types.Slice) + if !ok { + return false + } + elem := types.Unalias(typ.Elem()) + if version.Compare(LanguageVersion(pass, expr), "go1.18") >= 0 { + // Before Go 1.18, one could not directly convert from []T (where 'type T byte') + // to string. See also https://github.com/golang/go/issues/23536. + elem = elem.Underlying() + } + return types.Identical(elem, types.Typ[types.Byte]) +} + +func IsOfPointerToTypeWithName(pass *analysis.Pass, expr ast.Expr, name string) bool { + ptr, ok := types.Unalias(pass.TypesInfo.TypeOf(expr)).(*types.Pointer) + if !ok { + return false + } + return typeutil.IsTypeWithName(ptr.Elem(), name) +} + +func IsOfTypeWithName(pass *analysis.Pass, expr ast.Expr, name string) bool { + return typeutil.IsTypeWithName(pass.TypesInfo.TypeOf(expr), name) } func IsInTest(pass *analysis.Pass, node Positioner) bool { @@ -94,8 +119,9 @@ func BoolConst(pass *analysis.Pass, expr ast.Expr) bool { func IsBoolConst(pass *analysis.Pass, expr ast.Expr) bool { // We explicitly don't support typed bools because more often than - // not, custom bool types are used as binary enums and the - // explicit comparison is desired. + // not, custom bool types are used as binary enums and the explicit + // comparison is desired. We err on the side of false negatives and + // treat aliases like other custom types. ident, ok := expr.(*ast.Ident) if !ok { @@ -139,6 +165,9 @@ func ExprToString(pass *analysis.Pass, expr ast.Expr) (string, bool) { } func CallName(pass *analysis.Pass, call *ast.CallExpr) string { + // See the comment in typeutil.FuncName for why this doesn't require special handling + // of aliases. + fun := astutil.Unparen(call.Fun) // Instantiating a function cannot return another generic function, so doing this once is enough @@ -174,6 +203,9 @@ func CallName(pass *analysis.Pass, call *ast.CallExpr) string { } func IsCallTo(pass *analysis.Pass, node ast.Node, name string) bool { + // See the comment in typeutil.FuncName for why this doesn't require special handling + // of aliases. + call, ok := node.(*ast.CallExpr) if !ok { return false @@ -182,6 +214,9 @@ func IsCallTo(pass *analysis.Pass, node ast.Node, name string) bool { } func IsCallToAny(pass *analysis.Pass, node ast.Node, names ...string) bool { + // See the comment in typeutil.FuncName for why this doesn't require special handling + // of aliases. + call, ok := node.(*ast.CallExpr) if !ok { return false @@ -200,7 +235,91 @@ func File(pass *analysis.Pass, node Positioner) *ast.File { return m[pass.Fset.File(node.Pos())] } -// IsGenerated reports whether pos is in a generated file, It ignores +// BuildConstraints returns the build constraints for file f. It considers both //go:build lines as well as +// GOOS and GOARCH in file names. +func BuildConstraints(pass *analysis.Pass, f *ast.File) (constraint.Expr, bool) { + var expr constraint.Expr + for _, cmt := range f.Comments { + if len(cmt.List) == 0 { + continue + } + for _, el := range cmt.List { + if el.Pos() > f.Package { + break + } + if line := el.Text; strings.HasPrefix(line, "//go:build") { + var err error + expr, err = constraint.Parse(line) + if err != nil { + expr = nil + } + break + } + } + } + + name := pass.Fset.PositionFor(f.Pos(), false).Filename + oexpr := constraintsFromName(name) + if oexpr != nil { + if expr == nil { + expr = oexpr + } else { + expr = &constraint.AndExpr{X: expr, Y: oexpr} + } + } + + return expr, expr != nil +} + +func constraintsFromName(name string) constraint.Expr { + name = filepath.Base(name) + name = strings.TrimSuffix(name, ".go") + name = strings.TrimSuffix(name, "_test") + var goos, goarch string + switch strings.Count(name, "_") { + case 0: + // No GOOS or GOARCH in the file name. + case 1: + _, c, _ := strings.Cut(name, "_") + if _, ok := knowledge.KnownGOOS[c]; ok { + goos = c + } else if _, ok := knowledge.KnownGOARCH[c]; ok { + goarch = c + } + default: + n := strings.LastIndex(name, "_") + if _, ok := knowledge.KnownGOOS[name[n+1:]]; ok { + // The file name is *_stuff_GOOS.go + goos = name[n+1:] + } else if _, ok := knowledge.KnownGOARCH[name[n+1:]]; ok { + // The file name is *_GOOS_GOARCH.go or *_stuff_GOARCH.go + goarch = name[n+1:] + _, c, _ := strings.Cut(name[:n], "_") + if _, ok := knowledge.KnownGOOS[c]; ok { + // The file name is *_GOOS_GOARCH.go + goos = c + } + } else { + // The file name could also be something like foo_windows_nonsense.go — and because nonsense + // isn't a known GOARCH, "windows" won't be interpreted as a GOOS, either. + } + } + + var expr constraint.Expr + if goos != "" { + expr = &constraint.TagExpr{Tag: goos} + } + if goarch != "" { + if expr == nil { + expr = &constraint.TagExpr{Tag: goarch} + } else { + expr = &constraint.AndExpr{X: expr, Y: &constraint.TagExpr{Tag: goarch}} + } + } + return expr +} + +// IsGenerated reports whether pos is in a generated file. It ignores // //line directives. func IsGenerated(pass *analysis.Pass, pos token.Pos) bool { _, ok := Generator(pass, pos) @@ -315,13 +434,113 @@ func MayHaveSideEffects(pass *analysis.Pass, expr ast.Expr, purity purity.Result } } -func IsGoVersion(pass *analysis.Pass, minor int) bool { - f, ok := pass.Analyzer.Flags.Lookup("go").Value.(flag.Getter) - if !ok { - panic("requested Go version, but analyzer has no version flag") +// LanguageVersion returns the version of the Go language that node has access to. This +// might differ from the version of the Go standard library. +func LanguageVersion(pass *analysis.Pass, node Positioner) string { + // As of Go 1.21, two places can specify the minimum Go version: + // - 'go' directives in go.mod and go.work files + // - individual files by using '//go:build' + // + // Individual files can upgrade to a higher version than the module version. Individual files + // can also downgrade to a lower version, but only if the module version is at least Go 1.21. + // + // The restriction on downgrading doesn't matter to us. All language changes before Go 1.22 will + // not type-check on versions that are too old, and thus never reach our analyzes. In practice, + // such ineffective downgrading will always be useless, as the compiler will not restrict the + // language features used, and doesn't ever rely on minimum versions to restrict the use of the + // standard library. However, for us, both choices (respecting or ignoring ineffective + // downgrading) have equal complexity, but only respecting it has a non-zero chance of reducing + // noisy positives. + // + // The minimum Go versions are exposed via go/ast.File.GoVersion and go/types.Package.GoVersion. + // ast.File's version is populated by the parser, whereas types.Package's version is populated + // from the Go version specified in the types.Config, which is set by our package loader, based + // on the module information provided by go/packages, via 'go list -json'. + // + // As of Go 1.21, standard library packages do not present themselves as modules, and thus do + // not have a version set on their types.Package. In this case, we fall back to the version + // provided by our '-go' flag. In most cases, '-go' defaults to 'module', which falls back to + // the Go version that Staticcheck was built with when no module information exists. In the + // future, the standard library will hopefully be a proper module (see + // https://github.com/golang/go/issues/61174#issuecomment-1622471317). In that case, the version + // of standard library packages will match that of the used Go version. At that point, + // Staticcheck will refuse to work with Go versions that are too new, to avoid misinterpreting + // code due to language changes. + // + // We also lack module information when building in GOPATH mode. In this case, the implied + // language version is at most Go 1.21, as per https://github.com/golang/go/issues/60915. We + // don't handle this yet, and it will not matter until Go 1.22. + // + // It is not clear how per-file downgrading behaves in GOPATH mode. On the one hand, no module + // version at all is provided, which should preclude per-file downgrading. On the other hand, + // https://github.com/golang/go/issues/60915 suggests that the language version is at most 1.21 + // in GOPATH mode, which would allow per-file downgrading. Again it doesn't affect us, as all + // relevant language changes before Go 1.22 will lead to type-checking failures and never reach + // us. + // + // Per-file upgrading is permitted in GOPATH mode. + + // If the file has its own Go version, we will return that. Otherwise, we default to + // the type checker's GoVersion, which is populated from either the Go module, or from + // our '-go' flag. + return pass.TypesInfo.FileVersions[File(pass, node)] +} + +// StdlibVersion returns the version of the Go standard library that node can expect to +// have access to. This might differ from the language version for versions of Go older +// than 1.21. +func StdlibVersion(pass *analysis.Pass, node Positioner) string { + // The Go version as specified in go.mod or via the '-go' flag + n := pass.Pkg.GoVersion() + + f := File(pass, node) + if f == nil { + panic(fmt.Sprintf("no file found for node with position %s", pass.Fset.PositionFor(node.Pos(), false))) } - version := f.Get().(int) - return version >= minor + + if nf := f.GoVersion; nf != "" { + if version.Compare(n, "go1.21") == -1 { + // Before Go 1.21, the Go version set in go.mod specified the maximum language + // version available to the module. It wasn't uncommon to set the version to + // Go 1.20 but restrict usage of 1.20 functionality (both language and stdlib) + // to files tagged for 1.20, and supporting a lower version overall. As such, + // a file tagged lower than the module version couldn't expect to have access + // to the standard library of the version set in go.mod. + // + // At the same time, a file tagged higher than the module version, while not + // able to use newer language features, would still have been able to use a + // newer standard library. + // + // While Go 1.21's behavior has been backported to 1.19.11 and 1.20.6, users' + // expectations have not. + return nf + } else { + // Go 1.21 and newer refuse to build modules that depend on versions newer + // than the used version of the Go toolchain. This means that in a 1.22 module + // with a file tagged as 1.17, the file can expect to have access to 1.22's + // standard library (but not to 1.22 language features). A file tagged with a + // version higher than the minimum version has access to the newer standard + // library (and language features.) + // + // Do note that strictly speaking we're conflating the Go version and the + // module version in our check. Nothing is stopping a user from using Go 1.17 + // (which didn't implement the new rules for versions in go.mod) to build a Go + // 1.22 module, in which case a file tagged with go1.17 will not have acces to the 1.22 + // standard library. However, we believe that if a module requires 1.21 or + // newer, then the author clearly expects the new behavior, and doesn't care + // for the old one. Otherwise they would've specified an older version. + // + // In other words, the module version also specifies what it itself actually means, with + // >=1.21 being a minimum version for the toolchain, and <1.21 being a maximum version for + // the language. + + if version.Compare(nf, n) == 1 { + return nf + } + } + } + + return n } var integerLiteralQ = pattern.MustParse(`(IntegerLiteral tv)`) @@ -355,3 +574,20 @@ func IsMethod(pass *analysis.Pass, expr *ast.SelectorExpr, name string, meth *ty } return types.Identical(sel.Type(), meth) } + +func RefersTo(pass *analysis.Pass, expr ast.Expr, ident types.Object) bool { + found := false + fn := func(node ast.Node) bool { + ident2, ok := node.(*ast.Ident) + if !ok { + return true + } + if ident == pass.TypesInfo.ObjectOf(ident2) { + found = true + return false + } + return true + } + ast.Inspect(expr, fn) + return found +} diff --git a/tools/vendor/honnef.co/go/tools/analysis/facts/nilness/nilness.go b/tools/vendor/honnef.co/go/tools/analysis/facts/nilness/nilness.go index d296b5b0f7..17d344fab9 100644 --- a/tools/vendor/honnef.co/go/tools/analysis/facts/nilness/nilness.go +++ b/tools/vendor/honnef.co/go/tools/analysis/facts/nilness/nilness.go @@ -221,6 +221,8 @@ func impl(pass *analysis.Pass, fn *ir.Function, seenFns map[*ir.Function]struct{ return nilly case *ir.ChangeType: return mightReturnNil(v.X) + case *ir.MultiConvert: + return mightReturnNil(v.X) case *ir.Load: if _, ok := v.X.(*ir.Global); ok { return onlyGlobal @@ -230,11 +232,6 @@ func impl(pass *analysis.Pass, fn *ir.Function, seenFns map[*ir.Function]struct{ return neverNil case *ir.TypeAssert, *ir.ChangeInterface, *ir.Field, *ir.Const, *ir.GenericConst, *ir.Index, *ir.MapLookup, *ir.Parameter, *ir.Recv, *ir.TypeSwitch: return nilly - case *ir.CompositeValue: - // We can get here via composite literals of type parameters, for which typeutil.IsPointerLike doesn't - // currently return false (see https://staticcheck.io/issues/1364). However, we only emit ir.CompositeValue - // for value types, so we know it can't be nil. - return neverNil default: panic(fmt.Sprintf("internal error: unhandled type %T", v)) } @@ -243,6 +240,8 @@ func impl(pass *analysis.Pass, fn *ir.Function, seenFns map[*ir.Function]struct{ out := make([]neverNilness, len(ret.Results)) export := false for i, v := range ret.Results { + // OPT(dh): couldn't we check the result type's pointer-likeness early, and skip + // processing the return value altogether? v := mightReturnNil(v) out[i] = v if v != nilly && typeutil.IsPointerLike(fn.Signature.Results().At(i).Type()) { diff --git a/tools/vendor/honnef.co/go/tools/analysis/lint/lint.go b/tools/vendor/honnef.co/go/tools/analysis/lint/lint.go index 7d116a23d0..6f5861221c 100644 --- a/tools/vendor/honnef.co/go/tools/analysis/lint/lint.go +++ b/tools/vendor/honnef.co/go/tools/analysis/lint/lint.go @@ -3,16 +3,13 @@ package lint import ( - "flag" "fmt" "go/ast" - "go/build" "go/token" - "regexp" - "strconv" "strings" "golang.org/x/tools/go/analysis" + "honnef.co/go/tools/analysis/facts/tokenfile" ) // Analyzer wraps a go/analysis.Analyzer and provides structured documentation. @@ -20,33 +17,15 @@ type Analyzer struct { // The analyzer's documentation. Unlike go/analysis.Analyzer.Doc, // this field is structured, providing access to severity, options // etc. - Doc *Documentation + Doc *RawDocumentation Analyzer *analysis.Analyzer } -func (a *Analyzer) initialize() { - a.Analyzer.Doc = a.Doc.String() - if a.Analyzer.Flags.Usage == nil { - fs := flag.NewFlagSet("", flag.PanicOnError) - fs.Var(newVersionFlag(), "go", "Target Go version") - a.Analyzer.Flags = *fs - } -} - -// InitializeAnalyzers takes a map of documentation and a map of go/analysis.Analyzers and returns a slice of Analyzers. -// The map keys are the analyzer names. -func InitializeAnalyzers(docs map[string]*Documentation, analyzers map[string]*analysis.Analyzer) []*Analyzer { - out := make([]*Analyzer, 0, len(analyzers)) - for k, v := range analyzers { - v.Name = k - a := &Analyzer{ - Doc: docs[k], - Analyzer: v, - } - a.initialize() - out = append(out, a) - } - return out +func InitializeAnalyzer(a *Analyzer) *Analyzer { + a.Analyzer.Doc = a.Doc.Compile().String() + a.Analyzer.URL = "https://staticcheck.dev/docs/checks/#" + a.Analyzer.Name + a.Analyzer.Requires = append(a.Analyzer.Requires, tokenfile.Analyzer) + return a } // Severity describes the severity of diagnostics reported by an analyzer. @@ -97,26 +76,22 @@ type Documentation struct { MergeIf MergeStrategy } -func Markdownify(m map[string]*RawDocumentation) map[string]*Documentation { - out := make(map[string]*Documentation, len(m)) - for k, v := range m { - out[k] = &Documentation{ - Title: strings.TrimSpace(stripMarkdown(v.Title)), - Text: strings.TrimSpace(stripMarkdown(v.Text)), +func (doc RawDocumentation) Compile() *Documentation { + return &Documentation{ + Title: strings.TrimSpace(stripMarkdown(doc.Title)), + Text: strings.TrimSpace(stripMarkdown(doc.Text)), - TitleMarkdown: strings.TrimSpace(toMarkdown(v.Title)), - TextMarkdown: strings.TrimSpace(toMarkdown(v.Text)), + TitleMarkdown: strings.TrimSpace(toMarkdown(doc.Title)), + TextMarkdown: strings.TrimSpace(toMarkdown(doc.Text)), - Before: strings.TrimSpace(v.Before), - After: strings.TrimSpace(v.After), - Since: v.Since, - NonDefault: v.NonDefault, - Options: v.Options, - Severity: v.Severity, - MergeIf: v.MergeIf, - } + Before: strings.TrimSpace(doc.Before), + After: strings.TrimSpace(doc.After), + Since: doc.Since, + NonDefault: doc.NonDefault, + Options: doc.Options, + Severity: doc.Severity, + MergeIf: doc.MergeIf, } - return out } func toMarkdown(s string) string { @@ -191,51 +166,6 @@ func (doc *Documentation) String() string { return doc.Format(true) } -func newVersionFlag() flag.Getter { - tags := build.Default.ReleaseTags - v := tags[len(tags)-1][2:] - version := new(VersionFlag) - if err := version.Set(v); err != nil { - panic(fmt.Sprintf("internal error: %s", err)) - } - return version -} - -type VersionFlag int - -func (v *VersionFlag) String() string { - return fmt.Sprintf("1.%d", *v) -} - -var goVersionRE = regexp.MustCompile(`^(?:go)?1.(\d+).*$`) - -// ParseGoVersion parses Go versions of the form 1.M, 1.M.N, or 1.M.NrcR, with an optional "go" prefix. It assumes that -// versions have already been verified and are valid. -func ParseGoVersion(s string) (int, bool) { - m := goVersionRE.FindStringSubmatch(s) - if m == nil { - return 0, false - } - n, err := strconv.Atoi(m[1]) - if err != nil { - return 0, false - } - return n, true -} - -func (v *VersionFlag) Set(s string) error { - n, ok := ParseGoVersion(s) - if !ok { - return fmt.Errorf("invalid Go version: %q", s) - } - *v = VersionFlag(n) - return nil -} - -func (v *VersionFlag) Get() interface{} { - return int(*v) -} - // ExhaustiveTypeSwitch panics when called. It can be used to ensure // that type switches are exhaustive. func ExhaustiveTypeSwitch(v interface{}) { diff --git a/tools/vendor/honnef.co/go/tools/analysis/report/report.go b/tools/vendor/honnef.co/go/tools/analysis/report/report.go index d52945e72c..fcc2317ceb 100644 --- a/tools/vendor/honnef.co/go/tools/analysis/report/report.go +++ b/tools/vendor/honnef.co/go/tools/analysis/report/report.go @@ -6,10 +6,12 @@ import ( "go/ast" "go/format" "go/token" + "go/version" "path/filepath" "strconv" "strings" + "honnef.co/go/tools/analysis/code" "honnef.co/go/tools/analysis/facts/generated" "honnef.co/go/tools/go/ast/astutil" @@ -17,10 +19,14 @@ import ( ) type Options struct { - ShortRange bool - FilterGenerated bool - Fixes []analysis.SuggestedFix - Related []analysis.RelatedInformation + ShortRange bool + FilterGenerated bool + Fixes []analysis.SuggestedFix + Related []analysis.RelatedInformation + MinimumLanguageVersion string + MaximumLanguageVersion string + MinimumStdlibVersion string + MaximumStdlibVersion string } type Option func(*Options) @@ -58,6 +64,19 @@ func Related(node Positioner, message string) Option { } } +func MinimumLanguageVersion(vers string) Option { + return func(opts *Options) { opts.MinimumLanguageVersion = vers } +} +func MaximumLanguageVersion(vers string) Option { + return func(opts *Options) { opts.MinimumLanguageVersion = vers } +} +func MinimumStdlibVersion(vers string) Option { + return func(opts *Options) { opts.MinimumStdlibVersion = vers } +} +func MaximumStdlibVersion(vers string) Option { + return func(opts *Options) { opts.MaximumStdlibVersion = vers } +} + type Positioner interface { Pos() token.Pos } @@ -170,6 +189,21 @@ func Report(pass *analysis.Pass, node Positioner, message string, opts ...Option opt(cfg) } + langVersion := code.LanguageVersion(pass, node) + stdlibVersion := code.StdlibVersion(pass, node) + if n := cfg.MaximumLanguageVersion; n != "" && version.Compare(n, langVersion) == -1 { + return + } + if n := cfg.MaximumStdlibVersion; n != "" && version.Compare(n, stdlibVersion) == -1 { + return + } + if n := cfg.MinimumLanguageVersion; n != "" && version.Compare(n, langVersion) == 1 { + return + } + if n := cfg.MinimumStdlibVersion; n != "" && version.Compare(n, stdlibVersion) == 1 { + return + } + file := DisplayPosition(pass.Fset, node.Pos()).Filename if cfg.FilterGenerated { m := pass.ResultOf[generated.Analyzer].(map[string]generated.Generator) diff --git a/tools/vendor/honnef.co/go/tools/config/example.conf b/tools/vendor/honnef.co/go/tools/config/example.conf index b25c3a815f..dc102fbc07 100644 --- a/tools/vendor/honnef.co/go/tools/config/example.conf +++ b/tools/vendor/honnef.co/go/tools/config/example.conf @@ -1,4 +1,4 @@ -checks = ["all", "-ST1000", "-ST1003", "-ST1016", "-ST1020", "-ST1021", "-ST1022", "-ST1023"] +checks = ["all", "-SA9003", "-ST1000", "-ST1003", "-ST1016", "-ST1020", "-ST1021", "-ST1022", "-ST1023"] initialisms = ["ACL", "API", "ASCII", "CPU", "CSS", "DNS", "EOF", "GUID", "HTML", "HTTP", "HTTPS", "ID", "IP", "JSON", "QPS", "RAM", "RPC", "SLA", diff --git a/tools/vendor/honnef.co/go/tools/go/ast/astutil/util.go b/tools/vendor/honnef.co/go/tools/go/ast/astutil/util.go index e04e1fb0c6..342654409a 100644 --- a/tools/vendor/honnef.co/go/tools/go/ast/astutil/util.go +++ b/tools/vendor/honnef.co/go/tools/go/ast/astutil/util.go @@ -6,6 +6,8 @@ import ( "go/token" "reflect" "strings" + + "golang.org/x/tools/go/ast/astutil" ) func IsIdent(expr ast.Expr, ident string) bool { @@ -360,3 +362,112 @@ func Equal(a, b ast.Node) bool { panic(fmt.Sprintf("unreachable: %T", a)) } } + +func NegateDeMorgan(expr ast.Expr, recursive bool) ast.Expr { + switch expr := expr.(type) { + case *ast.BinaryExpr: + var out ast.BinaryExpr + switch expr.Op { + case token.EQL: + out.X = expr.X + out.Op = token.NEQ + out.Y = expr.Y + case token.LSS: + out.X = expr.X + out.Op = token.GEQ + out.Y = expr.Y + case token.GTR: + out.X = expr.X + out.Op = token.LEQ + out.Y = expr.Y + case token.NEQ: + out.X = expr.X + out.Op = token.EQL + out.Y = expr.Y + case token.LEQ: + out.X = expr.X + out.Op = token.GTR + out.Y = expr.Y + case token.GEQ: + out.X = expr.X + out.Op = token.LSS + out.Y = expr.Y + + case token.LAND: + out.X = NegateDeMorgan(expr.X, recursive) + out.Op = token.LOR + out.Y = NegateDeMorgan(expr.Y, recursive) + case token.LOR: + out.X = NegateDeMorgan(expr.X, recursive) + out.Op = token.LAND + out.Y = NegateDeMorgan(expr.Y, recursive) + } + return &out + + case *ast.ParenExpr: + if recursive { + return &ast.ParenExpr{ + X: NegateDeMorgan(expr.X, recursive), + } + } else { + return &ast.UnaryExpr{ + Op: token.NOT, + X: expr, + } + } + + case *ast.UnaryExpr: + if expr.Op == token.NOT { + return expr.X + } else { + return &ast.UnaryExpr{ + Op: token.NOT, + X: expr, + } + } + + default: + return &ast.UnaryExpr{ + Op: token.NOT, + X: expr, + } + } +} + +func SimplifyParentheses(node ast.Expr) ast.Expr { + var changed bool + // XXX accept list of ops to operate on + // XXX copy AST node, don't modify in place + post := func(c *astutil.Cursor) bool { + out := c.Node() + if paren, ok := c.Node().(*ast.ParenExpr); ok { + out = paren.X + } + + if binop, ok := out.(*ast.BinaryExpr); ok { + if right, ok := binop.Y.(*ast.BinaryExpr); ok && binop.Op == right.Op { + // XXX also check that Op is associative + + root := binop + pivot := root.Y.(*ast.BinaryExpr) + root.Y = pivot.X + pivot.X = root + root = pivot + out = root + } + } + + if out != c.Node() { + changed = true + c.Replace(out) + } + return true + } + + for changed = true; changed; { + changed = false + node = astutil.Apply(node, nil, post).(ast.Expr) + } + + return node +} diff --git a/tools/vendor/honnef.co/go/tools/go/ir/UPSTREAM b/tools/vendor/honnef.co/go/tools/go/ir/UPSTREAM index e92b016b39..bfcf02856f 100644 --- a/tools/vendor/honnef.co/go/tools/go/ir/UPSTREAM +++ b/tools/vendor/honnef.co/go/tools/go/ir/UPSTREAM @@ -5,5 +5,5 @@ The changes are too many to list here, and it is best to consider this package i Upstream changes still get applied when they address bugs in portions of code we have inherited. The last upstream commit we've looked at was: -e854e0228e2ef1cc6e42bbfde1951925096a1272 +ac2946029ad3806349fa00546449da9f59320e89 diff --git a/tools/vendor/honnef.co/go/tools/go/ir/builder.go b/tools/vendor/honnef.co/go/tools/go/ir/builder.go index 82ca94ba14..192eaaa50f 100644 --- a/tools/vendor/honnef.co/go/tools/go/ir/builder.go +++ b/tools/vendor/honnef.co/go/tools/go/ir/builder.go @@ -30,6 +30,7 @@ import ( "go/constant" "go/token" "go/types" + "go/version" "os" "honnef.co/go/tools/analysis/lint" @@ -49,8 +50,35 @@ var ( tString = types.Typ[types.String] tUntypedNil = types.Typ[types.UntypedNil] tEface = types.NewInterfaceType(nil, nil).Complete() + tDeferStack = types.NewPointer(typeutil.NewDeferStack()) + + vDeferStack = &Builtin{ + name: "ssa:deferstack", + sig: types.NewSignatureType(nil, nil, nil, nil, types.NewTuple(anonVar(tDeferStack)), false), + } ) +// range-over-func jump is READY +func jReady() *Const { + c := intConst(0, nil) + c.comment = "rangefunc.exit.ready" + return c +} + +// range-over-func jump is BUSY +func jBusy() *Const { + c := intConst(-1, nil) + c.comment = "rangefunc.exit.busy" + return c +} + +// range-over-func jump is DONE +func jDone() *Const { + c := intConst(-2, nil) + c.comment = "rangefunc.exit.done" + return c +} + // builder holds state associated with the package currently being built. // Its methods contain all the logic for AST-to-IR conversion. type builder struct { @@ -114,11 +142,11 @@ func (b *builder) logicalBinop(fn *Function, e *ast.BinaryExpr) Value { switch e.Op { case token.LAND: b.cond(fn, e.X, rhs, done) - short = emitConst(fn, NewConst(constant.MakeBool(false), t)) + short = emitConst(fn, NewConst(constant.MakeBool(false), t, e)) case token.LOR: b.cond(fn, e.X, done, rhs) - short = emitConst(fn, NewConst(constant.MakeBool(true), t)) + short = emitConst(fn, NewConst(constant.MakeBool(true), t, e)) } // Is rhs unreachable? @@ -149,6 +177,7 @@ func (b *builder) logicalBinop(fn *Function, e *ast.BinaryExpr) Value { phi := &Phi{Edges: edges} phi.typ = t + phi.comment = e.Op.String() return done.emit(phi, e) } @@ -222,9 +251,8 @@ func (b *builder) builtin(fn *Function, obj *types.Builtin, args []ast.Expr, typ // treat make([]T, n, m) as new([m]T)[:n] cap := m.Int64() at := types.NewArray(styp.Underlying().(*types.Slice).Elem(), cap) - alloc := emitNew(fn, at, source) v := &Slice{ - X: alloc, + X: emitNew(fn, at, source, "makeslice"), High: n, } v.setType(typ) @@ -247,7 +275,7 @@ func (b *builder) builtin(fn *Function, obj *types.Builtin, args []ast.Expr, typ return fn.emit(v, source) case *types.Chan: - var sz Value = emitConst(fn, intConst(0)) + var sz Value = emitConst(fn, intConst(0, source)) if len(args) == 2 { sz = b.expr(fn, args[1]) } @@ -260,8 +288,7 @@ func (b *builder) builtin(fn *Function, obj *types.Builtin, args []ast.Expr, typ } case "new": - alloc := emitNew(fn, deref(typ), source) - return alloc + return emitNew(fn, deref(typ), source, "new") case "len", "cap": // Special case: len or cap of an array or *array is based on the type, not the value which may be nil. We must @@ -271,12 +298,12 @@ func (b *builder) builtin(fn *Function, obj *types.Builtin, args []ast.Expr, typ // For example, for len(gen()), we need to evaluate gen() for its side-effects, but don't need the returned // value to determine the length of the array, which is constant. // - // This never applies to type parameters. Even if the constraint has a structural type, len/cap on a type - // parameter aren't constant. - t := deref(fn.Pkg.typeOf(args[0])).Underlying() + // Technically this shouldn't apply to type parameters because their length/capacity is never constant. We still + // choose to treat them as constant so that users of the IR get the practically constant length for free. + t := typeutil.CoreType(deref(fn.Pkg.typeOf(args[0]))) if at, ok := t.(*types.Array); ok { b.expr(fn, args[0]) // for effects only - return emitConst(fn, intConst(at.Len())) + return emitConst(fn, intConst(at.Len(), args[0])) } // Otherwise treat as normal. @@ -286,7 +313,7 @@ func (b *builder) builtin(fn *Function, obj *types.Builtin, args []ast.Expr, typ }, source) addEdge(fn.currentBlock, fn.Exit) fn.currentBlock = fn.newBasicBlock("unreachable") - return emitConst(fn, NewConst(constant.MakeBool(true), tBool)) // any non-nil Value will do + return emitConst(fn, NewConst(constant.MakeBool(true), tBool, nil)) // any non-nil Value will do } return nil // treat all others as a regular function call } @@ -322,7 +349,7 @@ func (b *builder) addr(fn *Function, e ast.Expr, escaping bool) (RET lvalue) { obj := fn.Pkg.objectOf(e) v := fn.Prog.packageLevelValue(obj) // var (address) if v == nil { - v = fn.lookup(obj, escaping) + v = fn.lookup(obj.(*types.Var), escaping) } return &address{addr: v, expr: e} @@ -330,9 +357,9 @@ func (b *builder) addr(fn *Function, e ast.Expr, escaping bool) (RET lvalue) { t := deref(fn.Pkg.typeOf(e)) var v *Alloc if escaping { - v = emitNew(fn, t, e) + v = emitNew(fn, t, e, "complit") } else { - v = fn.addLocal(t, e) + v = emitLocal(fn, t, e, "complit") } var sb storebuf b.compLit(fn, v, e, true, &sb) @@ -506,7 +533,7 @@ func (b *builder) assign(fn *Function, loc lvalue, e ast.Expr, isZero bool, sb * } if _, ok := loc.(*address); ok { - if isInterface(loc.typ()) && !typeparams.IsTypeParam(loc.typ()) { + if types.IsInterface(loc.typ()) && !typeparams.IsTypeParam(loc.typ()) { // e.g. var x interface{} = T{...} // Can't in-place initialize an interface value. // Fall back to copying. @@ -558,7 +585,7 @@ func (b *builder) expr(fn *Function, e ast.Expr) Value { // Is expression a constant? if tv.Value != nil { - return emitConst(fn, NewConst(tv.Value, tv.Type)) + return emitConst(fn, NewConst(tv.Value, tv.Type, e)) } var v Value @@ -589,11 +616,14 @@ func (b *builder) expr0(fn *Function, e ast.Expr, tv types.TypeAndValue) Value { Pkg: fn.Pkg, Prog: fn.Prog, functionBody: new(functionBody), + goversion: fn.goversion, // share the parent's goversion } + fn2.uniq = fn.uniq // start from parent's unique values fn2.source = e fn.AnonFuncs = append(fn.AnonFuncs, fn2) fn2.initHTML(b.printFunc) b.buildFunction(fn2) + fn.uniq = fn2.uniq // resume after anon's unique values if fn2.FreeVars == nil { return fn2 } @@ -634,7 +664,7 @@ func (b *builder) expr0(fn *Function, e ast.Expr, tv types.TypeAndValue) Value { case token.AND: // &X --- potentially escaping. addr := b.addr(fn, e.X, true) if _, ok := unparen(e.X).(*ast.StarExpr); ok { - // &*p must panic if p is nil (http://golang.org/s/go12nil). + // &*p must panic if p is nil (https://golang.org/s/go12nil). // For simplicity, we'll just (suboptimally) rely // on the side effects of a load. // TODO(adonovan): emit dedicated nilcheck. @@ -717,7 +747,7 @@ func (b *builder) expr0(fn *Function, e ast.Expr, tv types.TypeAndValue) Value { case *types.Builtin: return &Builtin{name: obj.Name(), sig: tv.Type.(*types.Signature)} case *types.Nil: - return emitConst(fn, nilConst(tv.Type)) + return emitConst(fn, nilConst(tv.Type, e)) } // Package-level func or var? if v := fn.Prog.packageLevelValue(obj); v != nil { @@ -731,7 +761,7 @@ func (b *builder) expr0(fn *Function, e ast.Expr, tv types.TypeAndValue) Value { return v // (func) } // Local var. - return emitLoad(fn, fn.lookup(obj, false), e) // var (address) + return emitLoad(fn, fn.lookup(obj.(*types.Var), false), e) // var (address) case *ast.SelectorExpr: sel, ok := fn.Pkg.info.Selections[e] @@ -757,7 +787,7 @@ func (b *builder) expr0(fn *Function, e ast.Expr, tv types.TypeAndValue) Value { wantAddr := isPointer(rt) escaping := true v := b.receiver(fn, e.X, wantAddr, escaping, sel, e) - if isInterface(rt) { + if types.IsInterface(rt) { // If v has interface type I, // we must emit a check that v is non-nil. // We use: typeassert v.(I). @@ -899,7 +929,7 @@ func (b *builder) setCallFunc(fn *Function, e *ast.CallExpr, c *CallCommon) { wantAddr := isPointer(recv) escaping := true v := b.receiver(fn, selector.X, wantAddr, escaping, sel, selector) - if isInterface(recv) { + if types.IsInterface(recv) { // Invoke-mode call. // Methods in interfaces cannot have their own type parameters, so we needn't do anything for type @@ -999,16 +1029,16 @@ func (b *builder) emitCallArgs(fn *Function, sig *types.Signature, e *ast.CallEx st := sig.Params().At(np).Type().(*types.Slice) vt := st.Elem() if len(varargs) == 0 { - args = append(args, emitConst(fn, nilConst(st))) + args = append(args, emitConst(fn, nilConst(st, nil))) } else { // Replace a suffix of args with a slice containing it. at := types.NewArray(vt, int64(len(varargs))) - a := emitNew(fn, at, e) + a := emitNew(fn, at, e, "varargs") a.source = e for i, arg := range varargs { iaddr := &IndexAddr{ X: a, - Index: emitConst(fn, intConst(int64(i))), + Index: emitConst(fn, intConst(int64(i), nil)), } iaddr.setType(types.NewPointer(vt)) fn.emit(iaddr, e) @@ -1051,7 +1081,7 @@ func (b *builder) localValueSpec(fn *Function, spec *ast.ValueSpec) { // 1:1 assignment for i, id := range spec.Names { if !isBlankIdent(id) { - fn.addLocalForIdent(id) + emitLocalVar(fn, identVar(fn, id), id) } lval := b.addr(fn, id, false) // non-escaping b.assign(fn, lval, spec.Values[i], true, nil, spec) @@ -1062,7 +1092,7 @@ func (b *builder) localValueSpec(fn *Function, spec *ast.ValueSpec) { // Locals are implicitly zero-initialized. for _, id := range spec.Names { if !isBlankIdent(id) { - lhs := fn.addLocalForIdent(id) + lhs := emitLocalVar(fn, identVar(fn, id), id) if fn.debugInfo() { emitDebugRef(fn, id, lhs, true) } @@ -1074,7 +1104,7 @@ func (b *builder) localValueSpec(fn *Function, spec *ast.ValueSpec) { tuple := b.exprN(fn, spec.Values[0]) for i, id := range spec.Names { if !isBlankIdent(id) { - fn.addLocalForIdent(id) + emitLocalVar(fn, identVar(fn, id), id) lhs := b.addr(fn, id, false) // non-escaping lhs.store(fn, emitExtract(fn, tuple, i, id), id) } @@ -1094,8 +1124,8 @@ func (b *builder) assignStmt(fn *Function, lhss, rhss []ast.Expr, isDef bool, so var lval lvalue = blank{} if !isBlankIdent(lhs) { if isDef { - if obj := fn.Pkg.info.Defs[lhs.(*ast.Ident)]; obj != nil { - fn.addNamedLocal(obj, lhs) + if obj, ok := fn.Pkg.info.Defs[lhs.(*ast.Ident)].(*types.Var); ok { + emitLocalVar(fn, obj, lhs) isZero[i] = true } } @@ -1176,7 +1206,7 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero Values: make([]Value, t.NumFields()), } for i := 0; i < t.NumFields(); i++ { - v.Values[i] = emitConst(fn, zeroConst(t.Field(i).Type())) + v.Values[i] = emitConst(fn, zeroConst(t.Field(i).Type(), e)) } v.setType(typ) @@ -1214,8 +1244,7 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero switch t := t.(type) { case *types.Slice: at = types.NewArray(t.Elem(), b.arrayLen(fn, e.Elts)) - alloc := emitNew(fn, at, e) - array = alloc + array = emitNew(fn, at, e, "slicelit") case *types.Array: at = t array = addr @@ -1224,7 +1253,7 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero var final Value if len(e.Elts) == 0 { if !isZero { - zc := emitConst(fn, zeroConst(at)) + zc := emitConst(fn, zeroConst(at, e)) final = zc } } else { @@ -1233,7 +1262,7 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero v := &CompositeValue{ Values: make([]Value, at.Len()), } - zc := emitConst(fn, zeroConst(at.Elem())) + zc := emitConst(fn, zeroConst(at.Elem(), e)) for i := range v.Values { v.Values[i] = zc } @@ -1249,7 +1278,7 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero if idx != nil { idxval = idx.Int64() + 1 } - idx = emitConst(fn, intConst(idxval)).(*Const) + idx = emitConst(fn, intConst(idxval, e)).(*Const) } iaddr := &compositeElement{ @@ -1283,7 +1312,7 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero if idx != nil { idxval = idx.Int64() + 1 } - idx = emitConst(fn, intConst(idxval)).(*Const) + idx = emitConst(fn, intConst(idxval, e)).(*Const) } iaddr := &IndexAddr{ X: array, @@ -1312,7 +1341,7 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero } case *types.Map: - m := &MakeMap{Reserve: emitConst(fn, intConst(int64(len(e.Elts))))} + m := &MakeMap{Reserve: emitConst(fn, intConst(int64(len(e.Elts)), e))} m.setType(typ) fn.emit(m, e) for _, e := range e.Elts { @@ -1467,7 +1496,7 @@ func (b *builder) switchStmtDynamic(fn *Function, s *ast.SwitchStmt, label *lblo if s.Init != nil { b.stmt(fn, s.Init) } - kTrue := emitConst(fn, NewConst(constant.MakeBool(true), tBool)) + kTrue := emitConst(fn, NewConst(constant.MakeBool(true), tBool, nil)) var tagv Value = kTrue var tagSource ast.Node = s @@ -1478,7 +1507,8 @@ func (b *builder) switchStmtDynamic(fn *Function, s *ast.SwitchStmt, label *lblo // lifting only considers loads and stores, but we want different // sigma nodes for the different comparisons. use a temporary and // load it in every branch. - tag := fn.addLocal(tagv.Type(), tagSource) + tag := emitLocal(fn, tagv.Type(), tagSource, "switch.value") + tag.comment = "switch.tag" emitStore(fn, tag, tagv, tagSource) done := fn.newBasicBlock("switch.done") @@ -1580,7 +1610,7 @@ func (b *builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl default: panic("unreachable") } - tagPtr := fn.addLocal(tag.Type(), tag.Source()) + tagPtr := emitLocal(fn, tag.Type(), tag.Source(), "") emitStore(fn, tagPtr, tag, tag.Source()) // +1 in case there's no explicit default case @@ -1606,8 +1636,8 @@ func (b *builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl var default_ *ast.CaseClause for _, clause := range s.Body.List { cc := clause.(*ast.CaseClause) - if obj := fn.Pkg.info.Implicits[cc]; obj != nil { - fn.addNamedLocal(obj, cc) + if obj, ok := fn.Pkg.info.Implicits[cc].(*types.Var); ok { + emitLocalVar(fn, obj, cc) } if cc.List == nil { // default case @@ -1615,7 +1645,7 @@ func (b *builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl } else { for _, expr := range cc.List { tswtch.Conds = append(tswtch.Conds, fn.Pkg.typeOf(expr)) - cswtch.Conds = append(cswtch.Conds, emitConst(fn, intConst(int64(index)))) + cswtch.Conds = append(cswtch.Conds, emitConst(fn, intConst(int64(index), expr))) index++ } if len(cc.List) == 1 { @@ -1640,7 +1670,7 @@ func (b *builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl // default branch fn.currentBlock = entry fn.emit(tswtch, s) - cswtch.Conds = append(cswtch.Conds, emitConst(fn, intConst(int64(-1)))) + cswtch.Conds = append(cswtch.Conds, emitConst(fn, intConst(int64(-1), nil))) // in theory we should add a local and stores/loads for tswtch, to // generate sigma nodes in the branches. however, there isn't any // useful information we could possibly attach to it. @@ -1661,16 +1691,16 @@ func (b *builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl heads = append(heads, head) fn.currentBlock = head - if obj := fn.Pkg.info.Implicits[cc]; obj != nil { + if obj, ok := fn.Pkg.info.Implicits[cc].(*types.Var); ok { // In a switch y := x.(type), each case clause // implicitly declares a distinct object y. // In a single-type case, y has that type. // In multi-type cases, 'case nil' and default, // y has the same type as the interface operand. - l := fn.objects[obj] + l := fn.vars[obj] if rets[index] == tUntypedNil { - emitStore(fn, l, emitConst(fn, nilConst(tswtch.Tag.Type())), s.Assign) + emitStore(fn, l, emitConst(fn, nilConst(tswtch.Tag.Type(), nil)), s.Assign) } else { x := emitExtract(fn, tswtch, index+1, s.Assign) emitStore(fn, l, x, nil) @@ -1701,8 +1731,8 @@ func (b *builder) typeSwitchStmt(fn *Function, s *ast.TypeSwitchStmt, label *lbl tail: fn.targets, _break: done, } - if obj := fn.Pkg.info.Implicits[default_]; obj != nil { - l := fn.objects[obj] + if obj, ok := fn.Pkg.info.Implicits[default_].(*types.Var); ok { + l := fn.vars[obj] x := emitExtract(fn, tswtch, index+1, s.Assign) emitStore(fn, l, x, s) } @@ -1851,10 +1881,10 @@ func (b *builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) (no b.stmtList(fn, clause.Body) emitJump(fn, done, s) fn.targets = fn.targets.tail - swtch.Conds = append(swtch.Conds, emitConst(fn, intConst(-1))) + swtch.Conds = append(swtch.Conds, emitConst(fn, intConst(-1, nil))) continue } - swtch.Conds = append(swtch.Conds, emitConst(fn, intConst(int64(state)))) + swtch.Conds = append(swtch.Conds, emitConst(fn, intConst(int64(state), nil))) body := fn.newBasicBlock("select.body") fn.currentBlock = body bodies = append(bodies, body) @@ -1872,7 +1902,8 @@ func (b *builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) (no case *ast.AssignStmt: // x := <-states[state].Chan if comm.Tok == token.DEFINE { - fn.addLocalForIdent(comm.Lhs[0].(*ast.Ident)) + id := comm.Lhs[0].(*ast.Ident) + emitLocalVar(fn, identVar(fn, id), id) } x := b.addr(fn, comm.Lhs[0], false) // non-escaping v := emitExtract(fn, sel, r, comm) @@ -1883,7 +1914,8 @@ func (b *builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) (no if len(comm.Lhs) == 2 { // x, ok := ... if comm.Tok == token.DEFINE { - fn.addLocalForIdent(comm.Lhs[1].(*ast.Ident)) + id := comm.Lhs[1].(*ast.Ident) + emitLocalVar(fn, identVar(fn, id), id) } ok := b.addr(fn, comm.Lhs[1], false) // non-escaping ok.store(fn, emitExtract(fn, sel, 1, comm), comm) @@ -1907,6 +1939,16 @@ func (b *builder) selectStmt(fn *Function, s *ast.SelectStmt, label *lblock) (no // forStmt emits to fn code for the for statement s, optionally // labelled by label. func (b *builder) forStmt(fn *Function, s *ast.ForStmt, label *lblock) { + // Use forStmtGo122 instead if it applies. + if s.Init != nil { + if assign, ok := s.Init.(*ast.AssignStmt); ok && assign.Tok == token.DEFINE { + if version.Compare(fn.goversion, "go1.22") >= 0 { + b.forStmtGo122(fn, s, label) + return + } + } + } + // ...init... // jump loop // loop: @@ -1958,6 +2000,169 @@ func (b *builder) forStmt(fn *Function, s *ast.ForStmt, label *lblock) { fn.currentBlock = done } +// forStmtGo122 emits to fn code for the for statement s, optionally +// labelled by label. s must define its variables. +// +// This allocates once per loop iteration. This is only correct in +// GoVersions >= go1.22. +func (b *builder) forStmtGo122(fn *Function, s *ast.ForStmt, label *lblock) { + // i_outer = alloc[T] + // *i_outer = ...init... // under objects[i] = i_outer + // jump loop + // loop: + // i = phi [head: i_outer, loop: i_next] + // ...cond... // under objects[i] = i + // if cond goto body else done + // body: + // ...body... // under objects[i] = i (same as loop) + // jump post + // post: + // tmp = *i + // i_next = alloc[T] + // *i_next = tmp + // ...post... // under objects[i] = i_next + // goto loop + // done: + + init := s.Init.(*ast.AssignStmt) + startingBlocks := len(fn.Blocks) + + pre := fn.currentBlock // current block before starting + loop := fn.newBasicBlock("for.loop") // target of back-edge + body := fn.newBasicBlock("for.body") + post := fn.newBasicBlock("for.post") // target of 'continue' + done := fn.newBasicBlock("for.done") // target of 'break' + + // For each of the n loop variables, we create five SSA values, + // outer, phi, next, load, and store in pre, loop, and post. + // There is no limit on n. + type loopVar struct { + obj *types.Var + outer *Alloc + phi *Phi + load *Load + next *Alloc + store *Store + } + vars := make([]loopVar, len(init.Lhs)) + for i, lhs := range init.Lhs { + v := identVar(fn, lhs.(*ast.Ident)) + + fn.currentBlock = pre + outer := emitLocal(fn, v.Type(), lhs, v.Name()) + + fn.currentBlock = loop + phi := &Phi{} + phi.comment = v.Name() + phi.typ = outer.Type() + fn.emit(phi, lhs) + + fn.currentBlock = post + // If next is is local, it reuses the address and zeroes the old value so + // load before allocating next. + load := emitLoad(fn, phi, init) + next := emitLocal(fn, v.Type(), lhs, v.Name()) + store := emitStore(fn, next, load, s) + + phi.Edges = []Value{outer, next} // pre edge is emitted before post edge. + vars[i] = loopVar{v, outer, phi, load, next, store} + } + + // ...init... under fn.objects[v] = i_outer + fn.currentBlock = pre + for _, v := range vars { + fn.vars[v.obj] = v.outer + } + const isDef = false // assign to already-allocated outers + b.assignStmt(fn, init.Lhs, init.Rhs, isDef, s) + if label != nil { + label._break = done + label._continue = post + } + emitJump(fn, loop, s) + + // ...cond... under fn.objects[v] = i + fn.currentBlock = loop + for _, v := range vars { + fn.vars[v.obj] = v.phi + } + if s.Cond != nil { + b.cond(fn, s.Cond, body, done) + } else { + emitJump(fn, body, s) + } + + // ...body... under fn.objects[v] = i + fn.currentBlock = body + fn.targets = &targets{ + tail: fn.targets, + _break: done, + _continue: post, + } + b.stmt(fn, s.Body) + fn.targets = fn.targets.tail + emitJump(fn, post, s) + + // ...post... under fn.objects[v] = i_next + for _, v := range vars { + fn.vars[v.obj] = v.next + } + fn.currentBlock = post + if s.Post != nil { + b.stmt(fn, s.Post) + } + emitJump(fn, loop, s) // back-edge + fn.currentBlock = done + + // For each loop variable that does not escape, + // (the common case), fuse its next cells into its + // (local) outer cell as they have disjoint live ranges. + // + // It is sufficient to test whether i_next escapes, + // because its Heap flag will be marked true if either + // the cond or post expression causes i to escape + // (because escape distributes over phi). + var nlocals int + for _, v := range vars { + if !v.next.Heap { + nlocals++ + } + } + if nlocals > 0 { + replace := make(map[Value]Value, 2*nlocals) + dead := make(map[Instruction]bool, 4*nlocals) + for _, v := range vars { + if !v.next.Heap { + replace[v.next] = v.outer + replace[v.phi] = v.outer + dead[v.phi], dead[v.next], dead[v.load], dead[v.store] = true, true, true, true + } + } + + // Replace all uses of i_next and phi with i_outer. + // Referrers have not been built for fn yet so only update Instruction operands. + // We need only look within the blocks added by the loop. + var operands []*Value // recycle storage + for _, b := range fn.Blocks[startingBlocks:] { + for _, instr := range b.Instrs { + operands = instr.Operands(operands[:0]) + for _, ptr := range operands { + k := *ptr + if v := replace[k]; v != nil { + *ptr = v + } + } + } + } + + // Remove instructions for phi, load, and store. + // lift() will remove the unused i_next *Alloc. + isDead := func(i Instruction) bool { return dead[i] } + loop.Instrs = removeInstrsIf(loop.Instrs, isDead) + post.Instrs = removeInstrsIf(post.Instrs, isDead) + } +} + // rangeIndexed emits to fn the header for an integer-indexed loop // over array, *array or slice value x. // The v result is defined only if tv is non-nil. @@ -1984,13 +2189,15 @@ func (b *builder) rangeIndexed(fn *Function, x Value, tv types.Type, source ast. // // We store the length in an Alloc and load it on each iteration so that lifting produces the necessary σ nodes length := newVariable(fn, tInt, source) - if arr, ok := deref(x.Type()).Underlying().(*types.Array); ok && !typeparams.IsTypeParam(x.Type()) { + if arr, ok := typeutil.CoreType(deref(x.Type())).(*types.Array); ok { // For array or *array, the number of iterations is known statically thanks to the type. We avoid a data // dependence upon x, permitting later dead-code elimination if x is pure, static unrolling, etc. Ranging over a // nil *array may have >0 iterations. We still generate code for x, in case it has effects. // - // This intentionally misses type parameters with core types, because their length isn't technically constant. - length.store(emitConst(fn, intConst(arr.Len()))) + // We use the core type of x, even though the length of type parameters isn't constant as per the language + // specification. Just because len(x) isn't constant doesn't mean we can't emit IR that takes advantage of a + // known length. + length.store(emitConst(fn, intConst(arr.Len(), nil))) } else { // length = len(x). var c Call @@ -2000,8 +2207,8 @@ func (b *builder) rangeIndexed(fn *Function, x Value, tv types.Type, source ast. length.store(fn.emit(&c, source)) } - index := fn.addLocal(tInt, source) - emitStore(fn, index, emitConst(fn, intConst(-1)), source) + index := emitLocal(fn, tInt, source, "rangeindex") + emitStore(fn, index, emitConst(fn, intConst(-1, nil)), source) loop = fn.newBasicBlock("rangeindex.loop") emitJump(fn, loop, source) @@ -2010,7 +2217,7 @@ func (b *builder) rangeIndexed(fn *Function, x Value, tv types.Type, source ast. incr := &BinOp{ Op: token.ADD, X: emitLoad(fn, index, source), - Y: emitConst(fn, intConst(1)), + Y: emitConst(fn, intConst(1, nil)), } incr.setType(tInt) emitStore(fn, index, fn.emit(incr, source), source) @@ -2155,6 +2362,57 @@ func (b *builder) rangeChan(fn *Function, x Value, tk types.Type, source ast.Nod return } +// rangeInt emits to fn the header for a range loop with an integer operand. +// tk is the key value's type, or nil if the k result is not wanted. +// pos is the position of the "for" token. +func (b *builder) rangeInt(fn *Function, x Value, tk types.Type, source ast.Node) (k Value, loop, done *BasicBlock) { + // + // iter = 0 + // if 0 < x goto body else done + // loop: (target of continue) + // iter++ + // if iter < x goto body else done + // body: + // k = x + // ...body... + // jump loop + // done: (target of break) + + if b, ok := x.Type().(*types.Basic); ok && b.Info()&types.IsUntyped != 0 { + x = emitConv(fn, x, tInt, source) + } + + T := x.Type() + iter := emitLocal(fn, T, source, "rangeint.iter") + // x may be unsigned. Avoid initializing x to -1. + + body := fn.newBasicBlock("rangeint.body") + done = fn.newBasicBlock("rangeint.done") + emitIf(fn, emitCompare(fn, token.LSS, emitConst(fn, zeroConst(T, source)), x, source), body, done, source) + + loop = fn.newBasicBlock("rangeint.loop") + fn.currentBlock = loop + + incr := &BinOp{ + Op: token.ADD, + X: emitLoad(fn, iter, source), + Y: emitConv(fn, emitConst(fn, intConst(1, source)), T, source), + } + incr.setType(T) + emitStore(fn, iter, fn.emit(incr, source), source) + emitIf(fn, emitCompare(fn, token.LSS, incr, x, source), body, done, source) + fn.currentBlock = body + + if tk != nil { + // Integer types (int, uint8, etc.) are named and + // we know that k is assignable to x when tk != nil. + // This implies tk and T are identical so no conversion is needed. + k = emitLoad(fn, iter, source) + } + + return +} + type variable struct { alloc *Alloc fn *Function @@ -2192,21 +2450,29 @@ func (b *builder) rangeStmt(fn *Function, s *ast.RangeStmt, label *lblock, sourc tv = fn.Pkg.typeOf(s.Value) } - // If iteration variables are defined (:=), this - // occurs once outside the loop. - // - // Unlike a short variable declaration, a RangeStmt - // using := never redeclares an existing variable; it - // always creates a new one. - if s.Tok == token.DEFINE { + // create locals for s.Key and s.Value + createVars := func() { + // Unlike a short variable declaration, a RangeStmt + // using := never redeclares an existing variable; it + // always creates a new one. if tk != nil { - fn.addLocalForIdent(s.Key.(*ast.Ident)) + id := s.Key.(*ast.Ident) + emitLocalVar(fn, identVar(fn, id), id) } if tv != nil { - fn.addLocalForIdent(s.Value.(*ast.Ident)) + id := s.Value.(*ast.Ident) + emitLocalVar(fn, identVar(fn, id), id) } } + afterGo122 := version.Compare(fn.goversion, "go1.22") >= 0 + + if s.Tok == token.DEFINE && !afterGo122 { + // pre-go1.22: If iteration variables are defined (:=), this + // occurs once outside the loop. + createVars() + } + x := b.expr(fn, s.X) var k, v Value @@ -2218,13 +2484,38 @@ func (b *builder) rangeStmt(fn *Function, s *ast.RangeStmt, label *lblock, sourc case *types.Chan: k, loop, done = b.rangeChan(fn, x, tk, source) - case *types.Map, *types.Basic: // string + case *types.Map: k, v, loop, done = b.rangeIter(fn, x, tk, tv, source) + case *types.Basic: + switch { + case rt.Info()&types.IsString != 0: + k, v, loop, done = b.rangeIter(fn, x, tk, tv, source) + + case rt.Info()&types.IsInteger != 0: + k, loop, done = b.rangeInt(fn, x, tk, source) + + default: + panic("Cannot range over basic type: " + rt.String()) + } + + case *types.Signature: + // Special case rewrite (fn.goversion >= go1.23): + // for x := range f { ... } + // into + // f(func(x T) bool { ... }) + b.rangeFunc(fn, x, tk, tv, s, label) + return + default: panic("Cannot range over: " + rt.String()) } + if s.Tok == token.DEFINE && afterGo122 { + // go1.22: If iteration variables are defined (:=), this occurs inside the loop. + createVars() + } + // Evaluate both LHS expressions before we update either. var kl, vl lvalue if tk != nil { @@ -2256,6 +2547,283 @@ func (b *builder) rangeStmt(fn *Function, s *ast.RangeStmt, label *lblock, sourc fn.currentBlock = done } +// rangeFunc emits to fn code for the range-over-func rng.Body of the iterator +// function x, optionally labelled by label. It creates a new anonymous function +// yield for rng and builds the function. +func (b *builder) rangeFunc(fn *Function, x Value, tk, tv types.Type, rng *ast.RangeStmt, label *lblock) { + // Consider the SSA code for the outermost range-over-func in fn: + // + // func fn(...) (ret R) { + // ... + // for k, v = range x { + // ... + // } + // ... + // } + // + // The code emitted into fn will look something like this. + // + // loop: + // jump := READY + // y := make closure yield [ret, deferstack, jump, k, v] + // x(y) + // switch jump { + // [see resuming execution] + // } + // goto done + // done: + // ... + // + // where yield is a new synthetic yield function: + // + // func yield(_k tk, _v tv) bool + // free variables: [ret, stack, jump, k, v] + // { + // entry: + // if jump != READY then goto invalid else valid + // invalid: + // panic("iterator called when it is not in a ready state") + // valid: + // jump = BUSY + // k = _k + // v = _v + // ... + // cont: + // jump = READY + // return true + // } + // + // Yield state: + // + // Each range loop has an associated jump variable that records + // the state of the iterator. A yield function is initially + // in a READY (0) and callable state. If the yield function is called + // and is not in READY state, it panics. When it is called in a callable + // state, it becomes BUSY. When execution reaches the end of the body + // of the loop (or a continue statement targeting the loop is executed), + // the yield function returns true and resumes being in a READY state. + // After the iterator function x(y) returns, then if the yield function + // is in a READY state, the yield enters the DONE state. + // + // Each lowered control statement (break X, continue X, goto Z, or return) + // that exits the loop sets the variable to a unique positive EXIT value, + // before returning false from the yield function. + // + // If the yield function returns abruptly due to a panic or GoExit, + // it remains in a BUSY state. The generated code asserts that, after + // the iterator call x(y) returns normally, the jump variable state + // is DONE. + // + // Resuming execution: + // + // The code generated for the range statement checks the jump + // variable to determine how to resume execution. + // + // switch jump { + // case BUSY: panic("...") + // case DONE: goto done + // case READY: state = DONE; goto done + // case 123: ... // action for exit 123. + // case 456: ... // action for exit 456. + // ... + // } + // + // Forward goto statements within a yield are jumps to labels that + // have not yet been traversed in fn. They may be in the Body of the + // function. What we emit for these is: + // + // goto target + // target: + // ... + // + // We leave an unresolved exit in yield.exits to check at the end + // of building yield if it encountered target in the body. If it + // encountered target, no additional work is required. Otherwise, + // the yield emits a new early exit in the basic block for target. + // We expect that blockopt will fuse the early exit into the case + // block later. The unresolved exit is then added to yield.parent.exits. + + loop := fn.newBasicBlock("rangefunc.loop") + done := fn.newBasicBlock("rangefunc.done") + + // These are targets within y. + fn.targets = &targets{ + tail: fn.targets, + _break: done, + // _continue is within y. + } + if label != nil { + label._break = done + // _continue is within y + } + + emitJump(fn, loop, nil) + fn.currentBlock = loop + + // loop: + // jump := READY + + anonIdx := len(fn.AnonFuncs) + + jump := newVar(fmt.Sprintf("jump$%d", anonIdx+1), tInt) + emitLocalVar(fn, jump, nil) // zero value is READY + + xsig := typeutil.CoreType(x.Type()).(*types.Signature) + ysig := typeutil.CoreType(xsig.Params().At(0).Type()).(*types.Signature) + + /* synthetic yield function for body of range-over-func loop */ + y := &Function{ + name: fmt.Sprintf("%s$%d", fn.Name(), anonIdx+1), + Signature: ysig, + Synthetic: SyntheticRangeOverFuncYield, + parent: fn, + Pkg: fn.Pkg, + Prog: fn.Prog, + functionBody: new(functionBody), + } + y.source = rng + y.goversion = fn.goversion + y.jump = jump + y.deferstack = fn.deferstack + y.returnVars = fn.returnVars // use the parent's return variables + y.uniq = fn.uniq // start from parent's unique values + + // If the RangeStmt has a label, this is how it is passed to buildYieldFunc. + if label != nil { + y.lblocks = map[*types.Label]*lblock{label.label: nil} + } + fn.AnonFuncs = append(fn.AnonFuncs, y) + + // Build y immediately. It may: + // * cause fn's locals to escape, and + // * create new exit nodes in exits. + // (y is not marked 'built' until the end of the enclosing FuncDecl.) + unresolved := len(fn.exits) + b.buildYieldFunc(y) + fn.uniq = y.uniq // resume after y's unique values + + // Emit the call of y. + // c := MakeClosure y + // x(c) + c := &MakeClosure{Fn: y} + c.setType(ysig) + c.comment = "yield" + for _, fv := range y.FreeVars { + c.Bindings = append(c.Bindings, fv.outer) + fv.outer = nil + } + fn.emit(c, nil) + call := Call{ + Call: CallCommon{ + Value: x, + Args: []Value{c}, + }, + } + call.setType(xsig.Results()) + fn.emit(&call, nil) + + exits := fn.exits[unresolved:] + b.buildYieldResume(fn, jump, exits, done) + + fn.currentBlock = done +} + +// buildYieldResume emits to fn code for how to resume execution once a call to +// the iterator function over the yield function returns x(y). It does this by building +// a switch over the value of jump for when it is READY, BUSY, or EXIT(id). +func (b *builder) buildYieldResume(fn *Function, jump *types.Var, exits []*exit, done *BasicBlock) { + // v := *jump + // switch v { + // case BUSY: panic("...") + // case READY: jump = DONE; goto done + // case EXIT(a): ... + // case EXIT(b): ... + // ... + // } + v := emitLoad(fn, fn.lookup(jump, false), nil) + + entry := fn.currentBlock + bodies := make([]*BasicBlock, 2, 2+len(exits)) + bodies[0] = fn.newBasicBlock("rangefunc.resume.busy") + bodies[1] = fn.newBasicBlock("rangefunc.resume.ready") + + conds := make([]Value, 2, 2+len(exits)) + conds[0] = emitConst(fn, jBusy()) + conds[1] = emitConst(fn, jReady()) + + fn.currentBlock = bodies[0] + fn.emit( + &Panic{ + X: emitConv(fn, emitConst(fn, stringConst("iterator call did not preserve panic", nil)), tEface, nil), + }, + nil, + ) + addEdge(fn.currentBlock, fn.Exit) + + fn.currentBlock = bodies[1] + storeVar(fn, jump, emitConst(fn, jDone()), nil) + emitJump(fn, done, nil) + + for _, e := range exits { + body := fn.newBasicBlock(fmt.Sprintf("rangefunc.resume.exit.%d", e.id)) + bodies = append(bodies, body) + id_ := intConst(e.id, nil) + id_.comment = fmt.Sprintf("rangefunc.exit.%d", e.id) + id := emitConst(fn, id_) + conds = append(conds, id) + + fn.currentBlock = body + switch { + case e.label != nil: // forward goto? + // case EXIT(id): goto lb // label + lb := fn.lblockOf(e.label) + // Do not mark lb as resolved. + // If fn does not contain label, lb remains unresolved and + // fn must itself be a range-over-func function. lb will be: + // lb: + // fn.jump = id + // return false + emitJump(fn, lb._goto, e.source) + + case e.to != fn: // e jumps to an ancestor of fn? + // case EXIT(id): { fn.jump = id; return false } + // fn is a range-over-func function. + + storeVar(fn, fn.jump, id, e.source) + vFalse := emitConst(fn, NewConst(constant.MakeBool(false), tBool, e.source)) + emitReturn(fn, []Value{vFalse}, e.source) + + case e.block == nil && e.label == nil: // return from fn? + // case EXIT(id): { return ... } + + // The results have already been stored to variables in fn.results, so + // emitReturn doesn't have to do it again. + emitReturn(fn, nil, e.source) + + case e.block != nil: + // case EXIT(id): goto block + emitJump(fn, e.block, e.source) + + default: + panic("unreachable") + } + + } + + fn.currentBlock = entry + // Note that this switch does not have an implicit default case. This wouldn't be + // valid for a user-provided switch statement, but for range-over-func we know all + // possible values and we can avoid the impossible branch. + swtch := &ConstantSwitch{ + Tag: v, + Conds: conds, + } + fn.emit(swtch, nil) + for _, body := range bodies { + addEdge(entry, body) + } +} + // stmt lowers statement s to IR form, emitting code to fn. func (b *builder) stmt(fn *Function, _s ast.Stmt) { // The label of the current statement. If non-nil, its _goto @@ -2279,7 +2847,14 @@ start: } case *ast.LabeledStmt: - label = fn.labelledBlock(s.Label) + if s.Label.Name == "_" { + // Blank labels can't be the target of a goto, break, + // or continue statement, so we don't need a new block. + _s = s.Stmt + goto start + } + label = fn.lblockOf(fn.label(s.Label)) + label.resolved = true emitJump(fn, label._goto, s) fn.currentBlock = label._goto _s = s.Stmt @@ -2302,7 +2877,7 @@ start: op = token.SUB } loc := b.addr(fn, s.X, false) - b.assignOp(fn, loc, emitConst(fn, NewConst(constant.MakeInt64(1), loc.typ())), op, s) + b.assignOp(fn, loc, emitConst(fn, NewConst(constant.MakeInt64(1), loc.typ(), s)), op, s) case *ast.AssignStmt: switch s.Tok { @@ -2324,73 +2899,17 @@ start: case *ast.DeferStmt: // The "intrinsics" new/make/len/cap are forbidden here. // panic is treated like an ordinary function call. - v := Defer{} + deferstack := emitLoad(fn, fn.lookup(fn.deferstack, false), s) + v := Defer{_DeferStack: deferstack} b.setCall(fn, s.Call, &v.Call) fn.hasDefer = true fn.emit(&v, s) case *ast.ReturnStmt: - // TODO(dh): we could emit tighter position information by - // using the ith returned expression - - var results []Value - if len(s.Results) == 1 && fn.Signature.Results().Len() > 1 { - // Return of one expression in a multi-valued function. - tuple := b.exprN(fn, s.Results[0]) - ttuple := tuple.Type().(*types.Tuple) - for i, n := 0, ttuple.Len(); i < n; i++ { - results = append(results, - emitConv(fn, emitExtract(fn, tuple, i, s), - fn.Signature.Results().At(i).Type(), s)) - } - } else { - // 1:1 return, or no-arg return in non-void function. - for i, r := range s.Results { - v := emitConv(fn, b.expr(fn, r), fn.Signature.Results().At(i).Type(), s) - results = append(results, v) - } - } - - ret := fn.results() - for i, r := range results { - emitStore(fn, ret[i], r, s) - } - - emitJump(fn, fn.Exit, s) - fn.currentBlock = fn.newBasicBlock("unreachable") + b.returnStmt(fn, s) case *ast.BranchStmt: - var block *BasicBlock - switch s.Tok { - case token.BREAK: - if s.Label != nil { - block = fn.labelledBlock(s.Label)._break - } else { - for t := fn.targets; t != nil && block == nil; t = t.tail { - block = t._break - } - } - - case token.CONTINUE: - if s.Label != nil { - block = fn.labelledBlock(s.Label)._continue - } else { - for t := fn.targets; t != nil && block == nil; t = t.tail { - block = t._continue - } - } - - case token.FALLTHROUGH: - for t := fn.targets; t != nil && block == nil; t = t.tail { - block = t._fallthrough - } - - case token.GOTO: - block = fn.labelledBlock(s.Label)._goto - } - j := emitJump(fn, block, s) - j.comment = s.Tok.String() - fn.currentBlock = fn.newBasicBlock("unreachable") + b.branchStmt(fn, s) case *ast.BlockStmt: b.stmtList(fn, s.List) @@ -2442,6 +2961,106 @@ start: } } +func (b *builder) branchStmt(fn *Function, s *ast.BranchStmt) { + var block *BasicBlock + if s.Label == nil { + block = targetedBlock(fn, s.Tok) + } else { + target := fn.label(s.Label) + block = labelledBlock(fn, target, s.Tok) + if block == nil { // forward goto + lb := fn.lblockOf(target) + block = lb._goto // jump to lb._goto + if fn.jump != nil { + // fn is a range-over-func and the goto may exit fn. + // Create an exit and resolve it at the end of + // builder.buildYieldFunc. + labelExit(fn, target, s) + } + } + } + to := block.parent + + if to == fn { + emitJump(fn, block, s) + } else { // break outside of fn. + // fn must be a range-over-func + e := blockExit(fn, block, s) + id_ := intConst(e.id, s) + id_.comment = fmt.Sprintf("rangefunc.exit.%d", e.id) + id := emitConst(fn, id_) + storeVar(fn, fn.jump, id, s) + vFalse := emitConst(fn, NewConst(constant.MakeBool(false), tBool, s)) + emitReturn(fn, []Value{vFalse}, s) + } + fn.currentBlock = fn.newBasicBlock("unreachable") +} + +func (b *builder) returnStmt(fn *Function, s *ast.ReturnStmt) { + // TODO(dh): we could emit tighter position information by + // using the ith returned expression + + var results []Value + + sig := fn.sourceFn.Signature // signature of the enclosing source function + + // Convert return operands to result type. + if len(s.Results) == 1 && sig.Results().Len() > 1 { + // Return of one expression in a multi-valued function. + tuple := b.exprN(fn, s.Results[0]) + ttuple := tuple.Type().(*types.Tuple) + for i, n := 0, ttuple.Len(); i < n; i++ { + results = append(results, + emitConv(fn, emitExtract(fn, tuple, i, s), + sig.Results().At(i).Type(), s)) + } + } else { + // 1:1 return, or no-arg return in non-void function. + for i, r := range s.Results { + v := emitConv(fn, b.expr(fn, r), sig.Results().At(i).Type(), s) + results = append(results, v) + } + } + + // Store the results. + for i, r := range results { + var result Value // fn.sourceFn.result[i] conceptually + if fn == fn.sourceFn { + result = fn.results[i] + } else { // lookup needed? + result = fn.lookup(fn.returnVars[i], false) + } + emitStore(fn, result, r, s) + } + + if fn.jump != nil { + // Return from body of a range-over-func. + // The return statement is syntactically within the loop, + // but the generated code is in the 'switch jump {...}' after it. + e := returnExit(fn, s) + id_ := intConst(e.id, e.source) + id_.comment = fmt.Sprintf("rangefunc.exit.%d", e.id) + id := emitConst(fn, id_) + storeVar(fn, fn.jump, id, e.source) + vFalse := emitConst(fn, NewConst(constant.MakeBool(false), tBool, e.source)) + emitReturn(fn, []Value{vFalse}, e.source) + return + } + + // The results have already been stored to variables in fn.results, so + // emitReturn doesn't have to do it again. + emitReturn(fn, nil, s) +} + +func emitReturn(fn *Function, results []Value, source ast.Node) { + for i, r := range results { + emitStore(fn, fn.results[i], r, source) + } + + emitJump(fn, fn.Exit, source) + fn.currentBlock = fn.newBasicBlock("unreachable") +} + // buildFunction builds IR code for the body of function fn. Idempotent. func (b *builder) buildFunction(fn *Function) { if fn.Blocks != nil { @@ -2487,12 +3106,12 @@ func (b *builder) buildFunction(fn *Function) { // code to reference them. This simplifies clients. if recv := fn.Signature.Recv(); recv != nil { // XXX synthesize an ast.Node - fn.addParamObj(recv, nil) + fn.addParamVar(recv, nil) } params := fn.Signature.Params() for i, n := 0, params.Len(); i < n; i++ { // XXX synthesize an ast.Node - fn.addParamObj(params.At(i), nil) + fn.addParamVar(params.At(i), nil) } } return @@ -2502,8 +3121,10 @@ func (b *builder) buildFunction(fn *Function) { } fn.blocksets = b.blocksets fn.Blocks = make([]*BasicBlock, 0, avgBlocks) + fn.sourceFn = fn fn.startBody() fn.createSyntacticParams(recvField, functype) + fn.createDeferStack() fn.exitBlock() b.stmt(fn, body) if cb := fn.currentBlock; cb != nil && (cb == fn.Blocks[0] || cb.Preds != nil) { @@ -2527,13 +3148,161 @@ func (b *builder) buildFunction(fn *Function) { fn.functionBody = nil } +// buildYieldFunc builds the body of the yield function created +// from a range-over-func *ast.RangeStmt. +func (b *builder) buildYieldFunc(fn *Function) { + // See builder.rangeFunc for detailed documentation on how fn is set up. + // + // In psuedo-Go this roughly builds: + // func yield(_k tk, _v tv) bool { + // if jump != READY { panic("yield function called after range loop exit") } + // jump = BUSY + // k, v = _k, _v // assign the iterator variable (if needed) + // ... // rng.Body + // continue: + // jump = READY + // return true + // } + s := fn.source.(*ast.RangeStmt) + fn.sourceFn = fn.parent.sourceFn + fn.startBody() + params := fn.Signature.Params() + for i := 0; i < params.Len(); i++ { + fn.addParamVar(params.At(i), nil) + } + fn.addResultVar(fn.Signature.Results().At(0), nil) + fn.exitBlock() + + // Initial targets + ycont := fn.newBasicBlock("yield-continue") + // lblocks is either {} or is {label: nil} where label is the label of syntax. + for label := range fn.lblocks { + fn.lblocks[label] = &lblock{ + label: label, + resolved: true, + _goto: ycont, + _continue: ycont, + // `break label` statement targets fn.parent.targets._break + } + } + fn.targets = &targets{ + _continue: ycont, + // `break` statement targets fn.parent.targets._break. + } + + // continue: + // jump = READY + // return true + saved := fn.currentBlock + fn.currentBlock = ycont + storeVar(fn, fn.jump, emitConst(fn, jReady()), s.Body) + vTrue := emitConst(fn, NewConst(constant.MakeBool(true), tBool, nil)) + emitReturn(fn, []Value{vTrue}, nil) + + // Emit header: + // + // if jump != READY { panic("yield iterator accessed after exit") } + // jump = BUSY + // k, v = _k, _v + fn.currentBlock = saved + yloop := fn.newBasicBlock("yield-loop") + invalid := fn.newBasicBlock("yield-invalid") + + jumpVal := emitLoad(fn, fn.lookup(fn.jump, true), nil) + emitIf(fn, emitCompare(fn, token.EQL, jumpVal, emitConst(fn, jReady()), nil), yloop, invalid, nil) + fn.currentBlock = invalid + fn.emit( + &Panic{ + X: emitConv(fn, emitConst(fn, stringConst("yield function called after range loop exit", nil)), tEface, nil), + }, + nil, + ) + addEdge(fn.currentBlock, fn.Exit) + + fn.currentBlock = yloop + storeVar(fn, fn.jump, emitConst(fn, jBusy()), s.Body) + + // Initialize k and v from params. + var tk, tv types.Type + if s.Key != nil && !isBlankIdent(s.Key) { + tk = fn.Pkg.typeOf(s.Key) // fn.parent.typeOf is identical + } + if s.Value != nil && !isBlankIdent(s.Value) { + tv = fn.Pkg.typeOf(s.Value) + } + if s.Tok == token.DEFINE { + if tk != nil { + emitLocalVar(fn, identVar(fn, s.Key.(*ast.Ident)), s.Key) + } + if tv != nil { + emitLocalVar(fn, identVar(fn, s.Value.(*ast.Ident)), s.Value) + } + } + var k, v Value + if len(fn.Params) > 0 { + k = fn.Params[0] + } + if len(fn.Params) > 1 { + v = fn.Params[1] + } + var kl, vl lvalue + if tk != nil { + kl = b.addr(fn, s.Key, false) // non-escaping + } + if tv != nil { + vl = b.addr(fn, s.Value, false) // non-escaping + } + if tk != nil { + kl.store(fn, k, s.Key) + } + if tv != nil { + vl.store(fn, v, s.Value) + } + + // Build the body of the range loop. + b.stmt(fn, s.Body) + if cb := fn.currentBlock; cb != nil && (cb == fn.Blocks[0] || cb.Preds != nil) { + // Control fell off the end of the function's body block. + // Block optimizations eliminate the current block, if + // unreachable. + emitJump(fn, ycont, nil) + } + + // Clean up exits and promote any unresolved exits to fn.parent. + for _, e := range fn.exits { + if e.label != nil { + lb := fn.lblocks[e.label] + if lb.resolved { + // label was resolved. Do not turn lb into an exit. + // e does not need to be handled by the parent. + continue + } + + // _goto becomes an exit. + // _goto: + // jump = id + // return false + fn.currentBlock = lb._goto + id_ := intConst(e.id, e.source) + id_.comment = fmt.Sprintf("rangefunc.exit.%d", e.id) + id := emitConst(fn, id_) + storeVar(fn, fn.jump, id, e.source) + vFalse := emitConst(fn, NewConst(constant.MakeBool(false), tBool, e.source)) + emitReturn(fn, []Value{vFalse}, e.source) + } + + if e.to != fn { // e needs to be handled by the parent too. + fn.parent.exits = append(fn.parent.exits, e) + } + } + + fn.finishBody() +} + // buildFuncDecl builds IR code for the function or method declared // by decl in package pkg. func (b *builder) buildFuncDecl(pkg *Package, decl *ast.FuncDecl) { id := decl.Name - if isBlankIdent(id) { - return // discard - } fn := pkg.values[pkg.info.Defs[id]].(*Function) if decl.Recv == nil && id.Name == "init" { var v Call @@ -2594,7 +3363,7 @@ func (p *Package) build() { done = init.Exit emitIf(init, emitLoad(init, initguard, nil), done, doinit, nil) init.currentBlock = doinit - emitStore(init, initguard, emitConst(init, NewConst(constant.MakeBool(true), tBool)), nil) + emitStore(init, initguard, emitConst(init, NewConst(constant.MakeBool(true), tBool, nil)), nil) // Call the init() function of each package we import. for _, pkg := range p.Pkg.Imports() { @@ -2618,6 +3387,12 @@ func (p *Package) build() { fmt.Fprintf(os.Stderr, "build global initializer %v @ %s\n", varinit.Lhs, p.Prog.Fset.Position(varinit.Rhs.Pos())) } + // Initializers for global vars are evaluated in dependency + // order, but may come from arbitrary files of the package + // with different versions, so we transiently update + // init.goversion for each one. (Since init is a synthetic + // function it has no syntax of its own that needs a version.) + init.goversion = p.initVersion[varinit.Rhs] if len(varinit.Lhs) == 1 { // 1:1 initialization: var x, y = a(), b() var lval lvalue @@ -2639,6 +3414,7 @@ func (p *Package) build() { } } } + init.goversion = "" // The rest of the init function is synthetic. No syntax => no goversion. // Build all package-level functions, init functions // and methods, including unreachable/blank ones. @@ -2655,7 +3431,9 @@ func (p *Package) build() { emitJump(init, done, nil) init.finishBody() - p.info = nil // We no longer need ASTs or go/types deductions. + // We no longer need ASTs or go/types deductions. + p.info = nil + p.initVersion = nil if p.Prog.mode&SanityCheckFunctions != 0 { sanityCheckPackage(p) diff --git a/tools/vendor/honnef.co/go/tools/go/ir/const.go b/tools/vendor/honnef.co/go/tools/go/ir/const.go index 0faf3852a6..7d27ec5774 100644 --- a/tools/vendor/honnef.co/go/tools/go/ir/const.go +++ b/tools/vendor/honnef.co/go/tools/go/ir/const.go @@ -8,6 +8,7 @@ package ir import ( "fmt" + "go/ast" "go/constant" "go/types" "strconv" @@ -19,37 +20,39 @@ import ( // NewConst returns a new constant of the specified value and type. // val must be valid according to the specification of Const.Value. -func NewConst(val constant.Value, typ types.Type) *Const { - return &Const{ +func NewConst(val constant.Value, typ types.Type, source ast.Node) *Const { + c := &Const{ register: register{ typ: typ, }, Value: val, } + c.setSource(source) + return c } // intConst returns an 'int' constant that evaluates to i. // (i is an int64 in case the host is narrower than the target.) -func intConst(i int64) *Const { - return NewConst(constant.MakeInt64(i), tInt) +func intConst(i int64, source ast.Node) *Const { + return NewConst(constant.MakeInt64(i), tInt, source) } // nilConst returns a nil constant of the specified type, which may // be any reference type, including interfaces. -func nilConst(typ types.Type) *Const { - return NewConst(nil, typ) +func nilConst(typ types.Type, source ast.Node) *Const { + return NewConst(nil, typ, source) } // stringConst returns a 'string' constant that evaluates to s. -func stringConst(s string) *Const { - return NewConst(constant.MakeString(s), tString) +func stringConst(s string, source ast.Node) *Const { + return NewConst(constant.MakeString(s), tString, source) } // zeroConst returns a new "zero" constant of the specified type. -func zeroConst(t types.Type) Constant { +func zeroConst(t types.Type, source ast.Node) Constant { if _, ok := t.Underlying().(*types.Interface); ok && !typeparams.IsTypeParam(t) { // Handle non-generic interface early to simplify following code. - return nilConst(t) + return nilConst(t, source) } tset := typeutil.NewTypeSet(t) @@ -58,21 +61,25 @@ func zeroConst(t types.Type) Constant { case *types.Struct: values := make([]Value, typ.NumFields()) for i := 0; i < typ.NumFields(); i++ { - values[i] = zeroConst(typ.Field(i).Type()) + values[i] = zeroConst(typ.Field(i).Type(), source) } - return &AggregateConst{ + ac := &AggregateConst{ register: register{typ: t}, Values: values, } + ac.setSource(source) + return ac case *types.Tuple: values := make([]Value, typ.Len()) for i := 0; i < typ.Len(); i++ { - values[i] = zeroConst(typ.At(i).Type()) + values[i] = zeroConst(typ.At(i).Type(), source) } - return &AggregateConst{ + ac := &AggregateConst{ register: register{typ: t}, Values: values, } + ac.setSource(source) + return ac } isNillable := func(term *types.Term) bool { @@ -108,20 +115,22 @@ func zeroConst(t types.Type) Constant { switch { case tset.All(isInfo(types.IsNumeric)): - return NewConst(constant.MakeInt64(0), t) + return NewConst(constant.MakeInt64(0), t, source) case tset.All(isInfo(types.IsString)): - return NewConst(constant.MakeString(""), t) + return NewConst(constant.MakeString(""), t, source) case tset.All(isInfo(types.IsBoolean)): - return NewConst(constant.MakeBool(false), t) + return NewConst(constant.MakeBool(false), t, source) case tset.All(isNillable): - return nilConst(t) + return nilConst(t, source) case tset.All(isArray): var k ArrayConst k.setType(t) + k.setSource(source) return &k default: var k GenericConst k.setType(t) + k.setSource(source) return &k } } @@ -246,7 +255,7 @@ func (c *Const) equal(o Constant) bool { if !ok { return false } - return c.typ == oc.typ && c.Value == oc.Value + return c.typ == oc.typ && c.Value == oc.Value && c.source == oc.source } func (c *AggregateConst) equal(o Constant) bool { @@ -258,6 +267,9 @@ func (c *AggregateConst) equal(o Constant) bool { if c.typ != oc.typ { return false } + if c.source != oc.source { + return false + } for i, v := range c.Values { if !v.(Constant).equal(oc.Values[i].(Constant)) { return false @@ -272,7 +284,7 @@ func (c *ArrayConst) equal(o Constant) bool { return false } // TODO(dh): don't use == for types, this will miss identical pointer types, among others - return c.typ == oc.typ + return c.typ == oc.typ && c.source == oc.source } func (c *GenericConst) equal(o Constant) bool { @@ -281,5 +293,5 @@ func (c *GenericConst) equal(o Constant) bool { return false } // TODO(dh): don't use == for types, this will miss identical pointer types, among others - return c.typ == oc.typ + return c.typ == oc.typ && c.source == oc.source } diff --git a/tools/vendor/honnef.co/go/tools/go/ir/create.go b/tools/vendor/honnef.co/go/tools/go/ir/create.go index 28e7da7e94..9aef3a5dfe 100644 --- a/tools/vendor/honnef.co/go/tools/go/ir/create.go +++ b/tools/vendor/honnef.co/go/tools/go/ir/create.go @@ -12,6 +12,7 @@ import ( "go/ast" "go/token" "go/types" + "go/version" "os" "sync" @@ -32,8 +33,6 @@ func NewProgram(fset *token.FileSet, mode BuilderMode) *Program { Fset: fset, imported: make(map[string]*Package), packages: make(map[*types.Package]*Package), - thunks: make(map[selectionKey]*Function), - bounds: make(map[*types.Func]*Function), mode: mode, } @@ -47,10 +46,10 @@ func NewProgram(fset *token.FileSet, mode BuilderMode) *Program { // memberFromObject populates package pkg with a member for the // typechecker object obj. // -// For objects from Go source code, syntax is the associated syntax -// tree (for funcs and vars only); it will be used during the build -// phase. -func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) { +// For objects from Go source code, syntax is the associated syntax tree +// (for funcs and vars only) and goversion defines the appropriate +// interpretation; they will be used during the build phase. +func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node, goversion string) { name := obj.Name() switch obj := obj.(type) { case *types.Builtin: @@ -59,19 +58,23 @@ func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) { } case *types.TypeName: - pkg.Members[name] = &Type{ - object: obj, - pkg: pkg, + if name != "_" { + pkg.Members[name] = &Type{ + object: obj, + pkg: pkg, + } } case *types.Const: c := &NamedConst{ object: obj, - Value: NewConst(obj.Val(), obj.Type()), + Value: NewConst(obj.Val(), obj.Type(), syntax), pkg: pkg, } pkg.values[obj] = c.Value - pkg.Members[name] = c + if name != "_" { + pkg.Members[name] = c + } case *types.Var: g := &Global{ @@ -81,7 +84,9 @@ func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) { typ: types.NewPointer(obj.Type()), // address } pkg.values[obj] = g - pkg.Members[name] = g + if name != "_" { + pkg.Members[name] = g + } case *types.Func: sig := obj.Type().(*types.Signature) @@ -95,6 +100,7 @@ func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) { Signature: sig, Pkg: pkg, Prog: pkg.Prog, + goversion: goversion, } fn.source = syntax @@ -114,7 +120,7 @@ func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) { pkg.values[obj] = fn pkg.Functions = append(pkg.Functions, fn) - if sig.Recv() == nil { + if name != "_" && sig.Recv() == nil { pkg.Members[name] = fn // package-level function } @@ -126,42 +132,46 @@ func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) { // membersFromDecl populates package pkg with members for each // typechecker object (var, func, const or type) associated with the // specified decl. -func membersFromDecl(pkg *Package, decl ast.Decl) { +func membersFromDecl(pkg *Package, decl ast.Decl, goversion string) { switch decl := decl.(type) { case *ast.GenDecl: // import, const, type or var switch decl.Tok { case token.CONST: for _, spec := range decl.Specs { for _, id := range spec.(*ast.ValueSpec).Names { - if !isBlankIdent(id) { - memberFromObject(pkg, pkg.info.Defs[id], nil) - } + memberFromObject(pkg, pkg.info.Defs[id], nil, "") } } case token.VAR: for _, spec := range decl.Specs { + for _, rhs := range spec.(*ast.ValueSpec).Values { + pkg.initVersion[rhs] = goversion + } for _, id := range spec.(*ast.ValueSpec).Names { - if !isBlankIdent(id) { - memberFromObject(pkg, pkg.info.Defs[id], spec) - } + memberFromObject(pkg, pkg.info.Defs[id], spec, goversion) } } case token.TYPE: for _, spec := range decl.Specs { id := spec.(*ast.TypeSpec).Name - if !isBlankIdent(id) { - memberFromObject(pkg, pkg.info.Defs[id], nil) - } + memberFromObject(pkg, pkg.info.Defs[id], nil, "") } } case *ast.FuncDecl: id := decl.Name - if !isBlankIdent(id) { - memberFromObject(pkg, pkg.info.Defs[id], decl) + obj, ok := pkg.info.Defs[id] + if !ok { + panic(fmt.Sprintf("couldn't find object for id %q at %s", + id.Name, pkg.Prog.Fset.PositionFor(id.Pos(), false))) + } + if obj == nil { + panic(fmt.Sprintf("found nil object for id %q at %s", + id.Name, pkg.Prog.Fset.PositionFor(id.Pos(), false))) } + memberFromObject(pkg, obj, decl, goversion) } } @@ -176,13 +186,15 @@ func membersFromDecl(pkg *Package, decl ast.Decl) { // until a subsequent call to Package.Build(). func (prog *Program) CreatePackage(pkg *types.Package, files []*ast.File, info *types.Info, importable bool) *Package { p := &Package{ - Prog: prog, - Members: make(map[string]Member), - values: make(map[types.Object]Value), - Pkg: pkg, - info: info, // transient (CREATE and BUILD phases) - files: files, // transient (CREATE and BUILD phases) - printFunc: prog.PrintFunc, + Prog: prog, + Members: make(map[string]Member), + values: make(map[types.Object]Value), + Pkg: pkg, + // transient values (CREATE and BUILD phases) + info: info, + files: files, + printFunc: prog.PrintFunc, + initVersion: make(map[ast.Expr]string), } // Add init() function. @@ -193,6 +205,7 @@ func (prog *Program) CreatePackage(pkg *types.Package, files []*ast.File, info * Pkg: p, Prog: prog, functionBody: new(functionBody), + goversion: "", // See Package.build for details. } p.init.initHTML(prog.PrintFunc) p.Members[p.init.name] = p.init @@ -203,8 +216,9 @@ func (prog *Program) CreatePackage(pkg *types.Package, files []*ast.File, info * if len(files) > 0 { // Go source package. for _, file := range files { + goversion := version.Lang(p.info.FileVersions[file]) for _, decl := range file.Decls { - membersFromDecl(p, decl) + membersFromDecl(p, decl, goversion) } } } else { @@ -214,11 +228,11 @@ func (prog *Program) CreatePackage(pkg *types.Package, files []*ast.File, info * scope := p.Pkg.Scope() for _, name := range scope.Names() { obj := scope.Lookup(name) - memberFromObject(p, obj, nil) + memberFromObject(p, obj, nil, "") if obj, ok := obj.(*types.TypeName); ok { if named, ok := obj.Type().(*types.Named); ok { for i, n := 0, named.NumMethods(); i < n; i++ { - memberFromObject(p, named.Method(i), nil) + memberFromObject(p, named.Method(i), nil, "") } } } diff --git a/tools/vendor/honnef.co/go/tools/go/ir/doc.go b/tools/vendor/honnef.co/go/tools/go/ir/doc.go index 5ee6637db4..e15ef60f45 100644 --- a/tools/vendor/honnef.co/go/tools/go/ir/doc.go +++ b/tools/vendor/honnef.co/go/tools/go/ir/doc.go @@ -10,9 +10,9 @@ // THIS INTERFACE IS EXPERIMENTAL AND IS LIKELY TO CHANGE. // // For an introduction to SSA form, upon which SSI builds, see -// http://en.wikipedia.org/wiki/Static_single_assignment_form. +// https://en.wikipedia.org/wiki/Static_single_assignment_form. // This page provides a broader reading list: -// http://www.dcs.gla.ac.uk/~jsinger/ssa.html. +// https://www.dcs.gla.ac.uk/~jsinger/ssa.html. // // For an introduction to SSI form, see The static single information // form by C. Scott Ananian. @@ -79,6 +79,7 @@ // *MakeSlice ✔ ✔ // *MapLookup ✔ ✔ // *MapUpdate ✔ ✔ +// *MultiConvert ✔ ✔ // *NamedConst ✔ (const) // *Next ✔ ✔ // *Panic ✔ diff --git a/tools/vendor/honnef.co/go/tools/go/ir/dom.go b/tools/vendor/honnef.co/go/tools/go/ir/dom.go index 4febd284ba..3f44c7c2a1 100644 --- a/tools/vendor/honnef.co/go/tools/go/ir/dom.go +++ b/tools/vendor/honnef.co/go/tools/go/ir/dom.go @@ -10,11 +10,11 @@ package ir // // We use the algorithm described in Lengauer & Tarjan. 1979. A fast // algorithm for finding dominators in a flowgraph. -// http://doi.acm.org/10.1145/357062.357071 +// https://doi.acm.org/10.1145/357062.357071 // // We also apply the optimizations to SLT described in Georgiadis et // al, Finding Dominators in Practice, JGAA 2006, -// http://jgaa.info/accepted/2006/GeorgiadisTarjanWerneck2006.10.1.pdf +// https://jgaa.info/accepted/2006/GeorgiadisTarjanWerneck2006.10.1.pdf // to avoid the need for buckets of size > 1. import ( diff --git a/tools/vendor/honnef.co/go/tools/go/ir/emit.go b/tools/vendor/honnef.co/go/tools/go/ir/emit.go index f6a1ef373b..e4304d10a9 100644 --- a/tools/vendor/honnef.co/go/tools/go/ir/emit.go +++ b/tools/vendor/honnef.co/go/tools/go/ir/emit.go @@ -18,15 +18,52 @@ import ( "golang.org/x/exp/typeparams" ) -// emitNew emits to f a new (heap Alloc) instruction allocating an -// object of type typ. pos is the optional source location. -func emitNew(f *Function, typ types.Type, source ast.Node) *Alloc { - v := &Alloc{Heap: true} +// emitAlloc emits to f a new Alloc instruction allocating a variable +// of type typ. +// +// The caller must set Alloc.Heap=true (for a heap-allocated variable) +// or add the Alloc to f.Locals (for a frame-allocated variable). +// +// During building, a variable in f.Locals may have its Heap flag +// set when it is discovered that its address is taken. +// These Allocs are removed from f.Locals at the end. +// +// The builder should generally call one of the emit{New,Local,LocalVar} wrappers instead. +func emitAlloc(f *Function, typ types.Type, source ast.Node, comment string) *Alloc { + v := &Alloc{} + v.comment = comment v.setType(types.NewPointer(typ)) f.emit(v, source) return v } +// emitNew emits to f a new Alloc instruction heap-allocating a +// variable of type typ. +func emitNew(f *Function, typ types.Type, source ast.Node, comment string) *Alloc { + alloc := emitAlloc(f, typ, source, comment) + alloc.Heap = true + return alloc +} + +// emitLocal creates a local var for (t, source, comment) and +// emits an Alloc instruction for it. +// +// (Use this function or emitNew for synthetic variables; +// for source-level variables, use emitLocalVar.) +func emitLocal(f *Function, t types.Type, source ast.Node, comment string) *Alloc { + local := emitAlloc(f, t, source, comment) + f.Locals = append(f.Locals, local) + return local +} + +// emitLocalVar creates a local var for v and emits an Alloc instruction for it. +// Subsequent calls to f.lookup(v) return it. +func emitLocalVar(f *Function, v *types.Var, source ast.Node) *Alloc { + alloc := emitLocal(f, v.Type(), source, v.Name()) + f.vars[v] = alloc + return alloc +} + // emitLoad emits to f an instruction to load the address addr into a // new temporary, and returns the value so defined. func emitLoad(f *Function, addr Value, source ast.Node) *Load { @@ -199,24 +236,17 @@ func emitConv(f *Function, val Value, t_dst types.Type, source ast.Node) Value { ut_dst := t_dst.Underlying() ut_src := t_src.Underlying() - tset_dst := typeutil.NewTypeSet(ut_dst) - tset_src := typeutil.NewTypeSet(ut_src) - - // Just a change of type, but not value or representation? - if tset_src.All(func(termSrc *types.Term) bool { - return tset_dst.All(func(termDst *types.Term) bool { - return isValuePreserving(termSrc.Type().Underlying(), termDst.Type().Underlying()) - }) - }) { - c := &ChangeType{X: val} - c.setType(t_dst) - return f.emit(c, source) - } - // Conversion to, or construction of a value of, an interface type? - if _, ok := ut_dst.(*types.Interface); ok && !typeparams.IsTypeParam(t_dst) { + if isNonTypeParamInterface(t_dst) { + // Interface name change? + if isValuePreserving(ut_src, ut_dst) { + c := &ChangeType{X: val} + c.setType(t_dst) + return f.emit(c, source) + } + // Assignment from one interface type to another? - if _, ok := ut_src.(*types.Interface); ok && !typeparams.IsTypeParam(t_src) { + if isNonTypeParamInterface(t_src) { c := &ChangeInterface{X: val} c.setType(t_dst) return f.emit(c, source) @@ -224,7 +254,7 @@ func emitConv(f *Function, val Value, t_dst types.Type, source ast.Node) Value { // Untyped nil constant? Return interface-typed nil constant. if ut_src == tUntypedNil { - return emitConst(f, nilConst(t_dst)) + return emitConst(f, zeroConst(t_dst, source)) } // Convert (non-nil) "untyped" literals to their default type. @@ -238,71 +268,130 @@ func emitConv(f *Function, val Value, t_dst types.Type, source ast.Node) Value { return f.emit(mi, source) } - // Conversion of a compile-time constant value? Note that converting a constant to a type parameter never results in - // a constant value. + // In the common case, the typesets of src and dst are singletons + // and we emit an appropriate conversion. But if either contains + // a type parameter, the conversion may represent a cross product, + // in which case which we emit a MultiConvert. + tset_dst := typeutil.NewTypeSet(ut_dst) + tset_src := typeutil.NewTypeSet(ut_src) + + // conversionCase describes an instruction pattern that may be emitted to + // model d <- s for d in dst_terms and s in src_terms. + // Multiple conversions can match the same pattern. + type conversionCase uint8 + const ( + changeType conversionCase = 1 << iota + sliceToArray + sliceToArrayPtr + sliceTo0Array + sliceTo0ArrayPtr + convert + ) + + classify := func(s, d types.Type) conversionCase { + // Just a change of type, but not value or representation? + if isValuePreserving(s, d) { + return changeType + } + + // Conversion from slice to array or slice to array pointer? + if slice, ok := s.(*types.Slice); ok { + var arr *types.Array + var ptr bool + // Conversion from slice to array pointer? + switch d := d.(type) { + case *types.Array: + arr = d + case *types.Pointer: + arr, _ = d.Elem().Underlying().(*types.Array) + ptr = true + } + if arr != nil && types.Identical(slice.Elem(), arr.Elem()) { + if arr.Len() == 0 { + if ptr { + return sliceTo0ArrayPtr + } else { + return sliceTo0Array + } + } + if ptr { + return sliceToArrayPtr + } else { + return sliceToArray + } + } + } + + // The only remaining case in well-typed code is a representation- + // changing conversion of basic types (possibly with []byte/[]rune). + if !isBasic(s) && !isBasic(d) { + panic(fmt.Sprintf("in %s: cannot convert term %s (%s [within %s]) to type %s [within %s]", f, val, val.Type(), s, t_dst, d)) + } + return convert + } + + var classifications conversionCase + for _, s := range tset_src.Terms { + us := s.Type().Underlying() + for _, d := range tset_dst.Terms { + ud := d.Type().Underlying() + classifications |= classify(us, ud) + } + } + if classifications == 0 { + panic(fmt.Sprintf("in %s: cannot convert %s (%s) to %s", f, val, val.Type(), t_dst)) + } + + // Conversion of a compile-time constant value? if c, ok := val.(*Const); ok { - if _, ok := ut_dst.(*types.Basic); ok || c.IsNil() { + // Conversion to a basic type? + if isBasic(ut_dst) { // Conversion of a compile-time constant to // another constant type results in a new // constant of the destination type and // (initially) the same abstract value. // We don't truncate the value yet. - return emitConst(f, NewConst(c.Value, t_dst)) + return emitConst(f, NewConst(c.Value, t_dst, source)) + } + // Can we always convert from zero value without panicking? + const mayPanic = sliceToArray | sliceToArrayPtr + if c.Value == nil && classifications&mayPanic == 0 { + return emitConst(f, NewConst(nil, t_dst, source)) } // We're converting from constant to non-constant type, // e.g. string -> []byte/[]rune. } - // Conversion from slice to array pointer? - if tset_src.All(func(termSrc *types.Term) bool { - return tset_dst.All(func(termDst *types.Term) bool { - if slice, ok := termSrc.Type().Underlying().(*types.Slice); ok { - if ptr, ok := termDst.Type().Underlying().(*types.Pointer); ok { - if arr, ok := ptr.Elem().Underlying().(*types.Array); ok && types.Identical(slice.Elem(), arr.Elem()) { - return true - } - } - } - return false - }) - }) { - c := &SliceToArrayPointer{X: val} + switch classifications { + case changeType: // representation-preserving change + c := &ChangeType{X: val} c.setType(t_dst) return f.emit(c, source) - } - // Conversion from slice to array. This is almost the same as converting from slice to array pointer, then - // dereferencing the pointer. Except that a nil slice can be converted to [0]T, whereas converting a nil slice to - // (*[0]T) results in a nil pointer, dereferencing which would panic. To hide the extra branching we use a dedicated - // instruction, SliceToArray. - if tset_src.All(func(termSrc *types.Term) bool { - return tset_dst.All(func(termDst *types.Term) bool { - if slice, ok := termSrc.Type().Underlying().(*types.Slice); ok { - if arr, ok := termDst.Type().Underlying().(*types.Array); ok && types.Identical(slice.Elem(), arr.Elem()) { - return true - } - } - return false - }) - }) { - c := &SliceToArray{X: val} + case sliceToArrayPtr, sliceTo0ArrayPtr: // slice to array pointer + c := &SliceToArrayPointer{X: val} c.setType(t_dst) return f.emit(c, source) - } - // A representation-changing conversion? - // At least one of {ut_src,ut_dst} must be *Basic. - // (The other may be []byte or []rune.) - ok1 := tset_src.Any(func(term *types.Term) bool { _, ok := term.Type().Underlying().(*types.Basic); return ok }) - ok2 := tset_dst.Any(func(term *types.Term) bool { _, ok := term.Type().Underlying().(*types.Basic); return ok }) - if ok1 || ok2 { + case sliceToArray: // slice to arrays (not zero-length) + p := &SliceToArray{X: val} + p.setType(t_dst) + return f.emit(p, source) + + case sliceTo0Array: // slice to zero-length arrays (constant) + return emitConst(f, zeroConst(t_dst, source)) + + case convert: // representation-changing conversion c := &Convert{X: val} c.setType(t_dst) return f.emit(c, source) - } - panic(fmt.Sprintf("in %s: cannot convert %s (%s) to %s", f, val, val.Type(), t_dst)) + default: // multiple conversion + c := &MultiConvert{X: val, from: tset_src, to: tset_dst} + c.setType(t_dst) + return f.emit(c, source) + } } // emitStore emits to f an instruction to store value val at location @@ -486,12 +575,13 @@ func emitFieldSelection(f *Function, v Value, index int, wantAddr bool, id *ast. // zeroValue emits to f code to produce a zero value of type t, // and returns it. func zeroValue(f *Function, t types.Type, source ast.Node) Value { - return emitConst(f, zeroConst(t)) + return emitConst(f, zeroConst(t, source)) } type constKey struct { - typ types.Type - value constant.Value + typ types.Type + value constant.Value + source ast.Node } func emitConst(f *Function, c Constant) Constant { @@ -529,8 +619,9 @@ func emitConst(f *Function, c Constant) Constant { panic(fmt.Sprintf("unexpected type %T", c)) } k := constKey{ - typ: typ, - value: val, + typ: typ, + value: val, + source: c.Source(), } dup, ok := f.consts[k] if ok { diff --git a/tools/vendor/honnef.co/go/tools/go/ir/func.go b/tools/vendor/honnef.co/go/tools/go/ir/func.go index 4449b405d2..923b04966d 100644 --- a/tools/vendor/honnef.co/go/tools/go/ir/func.go +++ b/tools/vendor/honnef.co/go/tools/go/ir/func.go @@ -176,77 +176,131 @@ type targets struct { // Destinations associated with a labelled block. // We populate these as labels are encountered in forward gotos or // labelled statements. +// Forward gotos are resolved once it is known which statement they +// are associated with inside the Function. type lblock struct { + label *types.Label // Label targeted by the blocks. + resolved bool // _goto block encountered (back jump or resolved fwd jump) _goto *BasicBlock _break *BasicBlock _continue *BasicBlock } -// labelledBlock returns the branch target associated with the -// specified label, creating it if needed. -func (f *Function) labelledBlock(label *ast.Ident) *lblock { - obj := f.Pkg.info.ObjectOf(label) - if obj == nil { - // Blank label, as in '_:' - don't store to f.lblocks, this label can never be referred to; just return a fresh - // lbock. - return &lblock{_goto: f.newBasicBlock(label.Name)} - } +// label returns the symbol denoted by a label identifier. +// +// label should be a non-blank identifier (label.Name != "_"). +func (f *Function) label(label *ast.Ident) *types.Label { + return f.Pkg.objectOf(label).(*types.Label) +} - lb := f.lblocks[obj] +// lblockOf returns the branch target associated with the +// specified label, creating it if needed. +func (f *Function) lblockOf(label *types.Label) *lblock { + lb := f.lblocks[label] if lb == nil { - lb = &lblock{_goto: f.newBasicBlock(label.Name)} + lb = &lblock{ + label: label, + _goto: f.newBasicBlock(label.Name()), + } if f.lblocks == nil { - f.lblocks = make(map[types.Object]*lblock) + f.lblocks = make(map[*types.Label]*lblock) } - f.lblocks[obj] = lb + f.lblocks[label] = lb } return lb } -// addParam adds a (non-escaping) parameter to f.Params of the -// specified name, type and source position. -func (f *Function) addParam(name string, typ types.Type, source ast.Node) *Parameter { - var b *BasicBlock - if len(f.Blocks) > 0 { - b = f.Blocks[0] +// labelledBlock searches f for the block of the specified label. +// +// If f is a yield function, it additionally searches ancestor Functions +// corresponding to enclosing range-over-func statements within the +// same source function, so the returned block may belong to a different Function. +func labelledBlock(f *Function, label *types.Label, tok token.Token) *BasicBlock { + if lb := f.lblocks[label]; lb != nil { + var block *BasicBlock + switch tok { + case token.BREAK: + block = lb._break + case token.CONTINUE: + block = lb._continue + case token.GOTO: + block = lb._goto + } + if block != nil { + return block + } } - v := &Parameter{ - name: name, + // Search ancestors if this is a yield function. + if f.jump != nil { + return labelledBlock(f.parent, label, tok) } - v.setBlock(b) - v.setType(typ) - v.setSource(source) - f.Params = append(f.Params, v) - if b != nil { - // There may be no blocks if this function has no body. We - // still create params, but aren't interested in the - // instruction. - f.Blocks[0].Instrs = append(f.Blocks[0].Instrs, v) + return nil +} + +// targetedBlock looks for the nearest block in f.targets +// (and f's ancestors) that matches tok's type, and returns +// the block and function it was found in. +func targetedBlock(f *Function, tok token.Token) *BasicBlock { + if f == nil { + return nil } - return v + for t := f.targets; t != nil; t = t.tail { + var block *BasicBlock + switch tok { + case token.BREAK: + block = t._break + case token.CONTINUE: + block = t._continue + case token.FALLTHROUGH: + block = t._fallthrough + } + if block != nil { + return block + } + } + // Search f's ancestors (in case f is a yield function). + return targetedBlock(f.parent, tok) +} + +// addResultVar adds a result for a variable v to f.results and v to f.returnVars. +func (f *Function) addResultVar(v *types.Var, source ast.Node) { + name := v.Name() + if name == "" { + name = fmt.Sprintf("res.%d", len(f.results)) + } + result := emitLocalVar(f, v, source) + result.comment = name + f.results = append(f.results, result) + f.returnVars = append(f.returnVars, v) } -func (f *Function) addParamObj(obj types.Object, source ast.Node) *Parameter { - name := obj.Name() +func (f *Function) addParamVar(v *types.Var, source ast.Node) *Parameter { + name := v.Name() if name == "" { name = fmt.Sprintf("arg%d", len(f.Params)) } - param := f.addParam(name, obj.Type(), source) - param.object = obj + var b *BasicBlock + if len(f.Blocks) > 0 { + b = f.Blocks[0] + } + param := &Parameter{name: name} + param.setBlock(b) + param.setType(v.Type()) + param.setSource(source) + param.object = v + f.Params = append(f.Params, param) + if b != nil { + f.Blocks[0].Instrs = append(f.Blocks[0].Instrs, param) + } return param } // addSpilledParam declares a parameter that is pre-spilled to the // stack; the function body will load/store the spilled location. // Subsequent lifting will eliminate spills where possible. -func (f *Function) addSpilledParam(obj types.Object, source ast.Node) { - param := f.addParamObj(obj, source) - spill := &Alloc{} - spill.setType(types.NewPointer(obj.Type())) - spill.source = source - f.objects[obj] = spill - f.Locals = append(f.Locals, spill) - f.emit(spill, source) +func (f *Function) addSpilledParam(obj *types.Var, source ast.Node) { + param := f.addParamVar(obj, source) + spill := emitLocalVar(f, obj, source) emitStore(f, spill, param, source) // f.emit(&Store{Addr: spill, Val: param}) } @@ -256,7 +310,7 @@ func (f *Function) addSpilledParam(obj types.Object, source ast.Node) { func (f *Function) startBody() { entry := f.newBasicBlock("entry") f.currentBlock = entry - f.objects = make(map[types.Object]Value) // needed for some synthetics, e.g. init + f.vars = make(map[*types.Var]Value) // needed for some synthetics, e.g. init } func (f *Function) blockset(i int) *BlockSet { @@ -280,12 +334,11 @@ func (f *Function) exitBlock() { f.Exit = f.newBasicBlock("exit") f.currentBlock = f.Exit - ret := f.results() - results := make([]Value, len(ret)) + results := make([]Value, len(f.results)) // Run function calls deferred in this // function when explicitly returning from it. f.emit(new(RunDefers), nil) - for i, r := range ret { + for i, r := range f.results { results[i] = emitLoad(f, r, nil) } @@ -306,11 +359,11 @@ func (f *Function) createSyntacticParams(recv *ast.FieldList, functype *ast.Func if recv != nil { for _, field := range recv.List { for _, n := range field.Names { - f.addSpilledParam(f.Pkg.info.Defs[n], n) + f.addSpilledParam(identVar(f, n), n) } // Anonymous receiver? No need to spill. if field.Names == nil { - f.addParamObj(f.Signature.Recv(), field) + f.addParamVar(f.Signature.Recv(), field) } } } @@ -320,35 +373,45 @@ func (f *Function) createSyntacticParams(recv *ast.FieldList, functype *ast.Func n := len(f.Params) // 1 if has recv, 0 otherwise for _, field := range functype.Params.List { for _, n := range field.Names { - f.addSpilledParam(f.Pkg.info.Defs[n], n) + f.addSpilledParam(identVar(f, n), n) } // Anonymous parameter? No need to spill. if field.Names == nil { - f.addParamObj(f.Signature.Params().At(len(f.Params)-n), field) + f.addParamVar(f.Signature.Params().At(len(f.Params)-n), field) } } } - // Named results. + // Results. if functype.Results != nil { for _, field := range functype.Results.List { // Implicit "var" decl of locals for named results. for _, n := range field.Names { - f.namedResults = append(f.namedResults, f.addLocalForIdent(n)) + v := identVar(f, n) + f.addResultVar(v, n) } - } - - if len(f.namedResults) == 0 { - sig := f.Signature.Results() - for i := 0; i < sig.Len(); i++ { - // XXX position information - v := f.addLocal(sig.At(i).Type(), nil) - f.implicitResults = append(f.implicitResults, v) + // Implicit "var" decl of local for an unnamed result. + if field.Names == nil { + v := f.Signature.Results().At(len(f.results)) + f.addResultVar(v, field.Type) } } } } +// createDeferStack initializes fn.deferstack to a local variable +// initialized to a ssa:deferstack() call. +func (fn *Function) createDeferStack() { + // Each syntactic function makes a call to ssa:deferstack, + // which is spilled to a local. Unused ones are later removed. + fn.deferstack = newVar("defer$stack", tDeferStack) + call := &Call{Call: CallCommon{Value: vDeferStack}} + call.setType(tDeferStack) + deferstack := fn.emit(call, nil) + spill := emitLocalVar(fn, fn.deferstack, nil) + emitStore(fn, spill, deferstack, nil) +} + func numberNodes(f *Function) { var base ID for _, b := range f.Blocks { @@ -508,7 +571,6 @@ buildLoop: // finishBody() finalizes the function after IR code generation of its body. func (f *Function) finishBody() { - f.objects = nil f.currentBlock = nil f.lblocks = nil @@ -549,8 +611,11 @@ func (f *Function) finishBody() { splitOnNewInformation(f.Blocks[0], &StackMap{}) } - f.namedResults = nil // (used by lifting) - f.implicitResults = nil + // clear remaining builder state + f.results = nil // (used by lifting) + f.deferstack = nil // (used by lifting) + f.vars = nil // (used by lifting) + f.goversion = "" numberNodes(f) @@ -627,37 +692,25 @@ func (f *Function) debugInfo() bool { return f.Pkg != nil && f.Pkg.debug } -// addNamedLocal creates a local variable, adds it to function f and -// returns it. Its name and type are taken from obj. Subsequent -// calls to f.lookup(obj) will return the same local. -func (f *Function) addNamedLocal(obj types.Object, source ast.Node) *Alloc { - l := f.addLocal(obj.Type(), source) - f.objects[obj] = l - return l -} - -func (f *Function) addLocalForIdent(id *ast.Ident) *Alloc { - return f.addNamedLocal(f.Pkg.info.Defs[id], id) -} - -// addLocal creates an anonymous local variable of type typ, adds it -// to function f and returns it. pos is the optional source location. -func (f *Function) addLocal(typ types.Type, source ast.Node) *Alloc { - v := &Alloc{} - v.setType(types.NewPointer(typ)) - f.Locals = append(f.Locals, v) - f.emit(v, source) - return v -} - // lookup returns the address of the named variable identified by obj // that is local to function f or one of its enclosing functions. // If escaping, the reference comes from a potentially escaping pointer // expression and the referent must be heap-allocated. -func (f *Function) lookup(obj types.Object, escaping bool) Value { - if v, ok := f.objects[obj]; ok { - if alloc, ok := v.(*Alloc); ok && escaping { - alloc.Heap = true +// We assume the referent is a *Alloc or *Phi. +// (The only Phis at this stage are those created directly by go1.22 "for" loops.) +func (f *Function) lookup(obj *types.Var, escaping bool) Value { + if v, ok := f.vars[obj]; ok { + if escaping { + switch v := v.(type) { + case *Alloc: + v.Heap = true + case *Phi: + for _, edge := range v.Edges { + if alloc, ok := edge.(*Alloc); ok { + alloc.Heap = true + } + } + } } return v // function-local var (address) } @@ -674,7 +727,7 @@ func (f *Function) lookup(obj types.Object, escaping bool) Value { outer: outer, parent: f, } - f.objects[obj] = v + f.vars[obj] = v f.FreeVars = append(f.FreeVars, v) return v } @@ -752,15 +805,15 @@ func (f *Function) relMethod(from *types.Package, recv types.Type) string { } // writeSignature writes to buf the signature sig in declaration syntax. -func writeSignature(buf *bytes.Buffer, from *types.Package, name string, sig *types.Signature, params []*Parameter) { +func writeSignature(buf *bytes.Buffer, from *types.Package, name string, sig *types.Signature) { buf.WriteString("func ") if recv := sig.Recv(); recv != nil { buf.WriteString("(") - if n := params[0].Name(); n != "" { - buf.WriteString(n) + if name := recv.Name(); name != "" { + buf.WriteString(name) buf.WriteString(" ") } - types.WriteType(buf, params[0].Type(), types.RelativeTo(from)) + types.WriteType(buf, recv.Type(), types.RelativeTo(from)) buf.WriteString(") ") } buf.WriteString(name) @@ -815,7 +868,7 @@ func WriteFunction(buf *bytes.Buffer, f *Function) { fmt.Fprintf(buf, "# % 3d:\t%s %s\n", i, l.Name(), relType(deref(l.Type()), from)) } } - writeSignature(buf, from, f.Name(), f.Signature, f.Params) + writeSignature(buf, from, f.Name(), f.Signature) buf.WriteString(":\n") if f.Blocks == nil { @@ -955,3 +1008,83 @@ func killInstruction(instr Instruction) { } } } + +// identVar returns the variable defined by id. +func identVar(fn *Function, id *ast.Ident) *types.Var { + return fn.Pkg.info.Defs[id].(*types.Var) +} + +// unique returns a unique positive int within the source tree of f. +// The source tree of f includes all of f's ancestors by parent and all +// of the AnonFuncs contained within these. +func unique(f *Function) int64 { + f.uniq++ + return f.uniq +} + +// exit is a change of control flow going from a range-over-func +// yield function to an ancestor function caused by a break, continue, +// goto, or return statement. +// +// There are 3 types of exits: +// * return from the source function (from ReturnStmt), +// * jump to a block (from break and continue statements [labelled/unlabelled]), +// * go to a label (from goto statements). +// +// As the builder does one pass over the ast, it is unclear whether +// a forward goto statement will leave a range-over-func body. +// The function being exited to is unresolved until the end +// of building the range-over-func body. +type exit struct { + id int64 // unique value for exit within from and to + from *Function // the function the exit starts from + to *Function // the function being exited to (nil if unresolved) + source ast.Node + + block *BasicBlock // basic block within to being jumped to. + label *types.Label // forward label being jumped to via goto. + // block == nil && label == nil => return +} + +// storeVar emits to function f code to store a value v to a *types.Var x. +func storeVar(f *Function, x *types.Var, v Value, source ast.Node) { + emitStore(f, f.lookup(x, true), v, source) +} + +// labelExit creates a new exit to a yield fn to exit the function using a label. +func labelExit(fn *Function, label *types.Label, source ast.Node) *exit { + e := &exit{ + id: unique(fn), + from: fn, + to: nil, + source: source, + label: label, + } + fn.exits = append(fn.exits, e) + return e +} + +// blockExit creates a new exit to a yield fn that jumps to a basic block. +func blockExit(fn *Function, block *BasicBlock, source ast.Node) *exit { + e := &exit{ + id: unique(fn), + from: fn, + to: block.parent, + source: source, + block: block, + } + fn.exits = append(fn.exits, e) + return e +} + +// returnExit creates a new exit to a yield fn that returns to the source function. +func returnExit(fn *Function, source ast.Node) *exit { + e := &exit{ + id: unique(fn), + from: fn, + to: fn.sourceFn, + source: source, + } + fn.exits = append(fn.exits, e) + return e +} diff --git a/tools/vendor/honnef.co/go/tools/go/ir/html.go b/tools/vendor/honnef.co/go/tools/go/ir/html.go index 35b421a70f..ae502db4e3 100644 --- a/tools/vendor/honnef.co/go/tools/go/ir/html.go +++ b/tools/vendor/honnef.co/go/tools/go/ir/html.go @@ -881,6 +881,9 @@ func valueLongHTML(v Node) string { for _, a := range v.Operands(nil) { s += fmt.Sprintf(" %s", valueHTML(*a)) } + if v, ok := v.(Instruction); ok { + s += fmt.Sprintf(" (%s)", v.Comment()) + } // OPT(dh): we're calling namedValues many times on the same function. allNames := namedValues(v.Parent()) diff --git a/tools/vendor/honnef.co/go/tools/go/ir/irutil/load.go b/tools/vendor/honnef.co/go/tools/go/ir/irutil/load.go index 883447b7af..bbf0e27191 100644 --- a/tools/vendor/honnef.co/go/tools/go/ir/irutil/load.go +++ b/tools/vendor/honnef.co/go/tools/go/ir/irutil/load.go @@ -146,12 +146,14 @@ func BuildPackage(tc *types.Config, fset *token.FileSet, pkg *types.Package, fil } info := &types.Info{ - Types: make(map[ast.Expr]types.TypeAndValue), - Defs: make(map[*ast.Ident]types.Object), - Uses: make(map[*ast.Ident]types.Object), - Implicits: make(map[ast.Node]types.Object), - Scopes: make(map[ast.Node]*types.Scope), - Selections: make(map[*ast.SelectorExpr]*types.Selection), + Types: make(map[ast.Expr]types.TypeAndValue), + Defs: make(map[*ast.Ident]types.Object), + Uses: make(map[*ast.Ident]types.Object), + Implicits: make(map[ast.Node]types.Object), + Scopes: make(map[ast.Node]*types.Scope), + Selections: make(map[*ast.SelectorExpr]*types.Selection), + Instances: make(map[*ast.Ident]types.Instance), + FileVersions: make(map[*ast.File]string), } if err := types.NewChecker(tc, fset, pkg, info).Files(files); err != nil { return nil, nil, err diff --git a/tools/vendor/honnef.co/go/tools/go/ir/lift.go b/tools/vendor/honnef.co/go/tools/go/ir/lift.go index 1a4cd3026d..08be0d3718 100644 --- a/tools/vendor/honnef.co/go/tools/go/ir/lift.go +++ b/tools/vendor/honnef.co/go/tools/go/ir/lift.go @@ -12,14 +12,14 @@ package ir // Cited papers and resources: // // Ron Cytron et al. 1991. Efficiently computing SSA form... -// http://doi.acm.org/10.1145/115372.115320 +// https://doi.acm.org/10.1145/115372.115320 // // Cooper, Harvey, Kennedy. 2001. A Simple, Fast Dominance Algorithm. // Software Practice and Experience 2001, 4:1-10. -// http://www.hipersoft.rice.edu/grads/publications/dom14.pdf +// https://www.hipersoft.rice.edu/grads/publications/dom14.pdf // // Daniel Berlin, llvmdev mailing list, 2012. -// http://lists.cs.uiuc.edu/pipermail/llvmdev/2012-January/046638.html +// https://lists.cs.uiuc.edu/pipermail/llvmdev/2012-January/046638.html // (Be sure to expand the whole thread.) // // C. Scott Ananian. 1997. The static single information form. @@ -46,6 +46,7 @@ import ( "encoding/binary" "fmt" "os" + "slices" ) // If true, show diagnostic information at each step of lifting. @@ -135,18 +136,11 @@ func buildPostDomFrontier(fn *Function) postDomFrontier { } func removeInstr(refs []Instruction, instr Instruction) []Instruction { - i := 0 - for _, ref := range refs { - if ref == instr { - continue - } - refs[i] = ref - i++ - } - for j := i; j != len(refs); j++ { - refs[j] = nil // aid GC - } - return refs[:i] + return removeInstrsIf(refs, func(i Instruction) bool { return i == instr }) +} + +func removeInstrsIf(refs []Instruction, p func(Instruction) bool) []Instruction { + return slices.DeleteFunc(refs, p) } func clearInstrs(instrs []Instruction) { @@ -208,9 +202,12 @@ func lift(fn *Function) bool { // for the block, reusing the original array if space permits. // While we're here, we also eliminate 'rundefers' - // instructions in functions that contain no 'defer' - // instructions. + // instructions and ssa:deferstack() in functions that contain no + // 'defer' instructions. Eliminate ssa:deferstack() if it does not + // escape. usesDefer := false + deferstackAlloc, deferstackCall := deferstackPreamble(fn) + eliminateDeferStack := deferstackAlloc != nil && !deferstackAlloc.Heap // Determine which allocs we can lift and number them densely. // The renaming phase uses this numbering for compact maps. @@ -262,6 +259,15 @@ func lift(fn *Function) bool { numAllocs++ case *Defer: usesDefer = true + if eliminateDeferStack { + // Clear _DeferStack and remove references to loads + if instr._DeferStack != nil { + if refs := instr._DeferStack.Referrers(); refs != nil { + *refs = removeInstr(*refs, instr) + } + instr._DeferStack = nil + } + } case *RunDefers: b.rundefers++ } @@ -316,6 +322,18 @@ func lift(fn *Function) bool { // Eliminate dead φ- and σ-nodes. markLiveNodes(fn.Blocks, newPhis, newSigmas) + + // Eliminate ssa:deferstack() call. + if eliminateDeferStack { + b := deferstackCall.block + for i, instr := range b.Instrs { + if instr == deferstackCall { + b.Instrs[i] = nil + b.gaps++ + break + } + } + } } // Prepend remaining live φ-nodes to each block and possibly kill rundefers. @@ -880,10 +898,10 @@ type liftInstructions struct { func liftable(alloc *Alloc, instructions BlockMap[liftInstructions]) bool { fn := alloc.block.parent - // Don't lift named return values in functions that defer + // Don't lift result values in functions that defer // calls that may recover from panic. if fn.hasDefer { - for _, nr := range fn.namedResults { + for _, nr := range fn.results { if nr == alloc { return false } @@ -1225,10 +1243,26 @@ func liftAlloc(closure *closure, df domFrontier, rdf postDomFrontier, alloc *All // Create φ-node. // It will be prepended to v.Instrs later, if needed. + if len(y.Preds) == 0 { + // The exit block may be unreachable if the function doesn't + // return, e.g. due to an infinite loop. In that case we + // should not replace loads in the exit block with ϕ node that + // have no edges. Such loads exist when the function has named + // return parameters, as the exit block loads them to turn + // them into a Return instruction. By not replacing the loads + // with ϕ nodes, they will later be replaced by zero + // constants. This is arguably more correct, and more + // importantly, it doesn't break code that assumes that phis + // have at least one edge. + // + // For one instance of breakage see + // https://staticcheck.dev/issues/1533 + continue + } phi := &Phi{ Edges: make([]Value, len(y.Preds)), } - + phi.comment = alloc.comment phi.source = alloc.source phi.setType(deref(alloc.Type())) phi.block = y @@ -1271,6 +1305,7 @@ func liftAlloc(closure *closure, df domFrontier, rdf postDomFrontier, alloc *All From: y, X: alloc, } + sigma.comment = alloc.comment sigma.source = alloc.source sigma.setType(deref(alloc.Type())) sigma.block = succ @@ -1359,7 +1394,7 @@ func replace(instr Instruction, x, y Value) { func renamed(fn *Function, renaming []Value, alloc *Alloc) Value { v := renaming[alloc.index] if v == nil { - v = emitConst(fn, zeroConst(deref(alloc.Type()))) + v = emitConst(fn, zeroConst(deref(alloc.Type()), alloc.source)) renaming[alloc.index] = v } return v @@ -1768,3 +1803,17 @@ func updateOperandReferrers(instr Instruction) { } } } + +// deferstackPreamble returns the *Alloc and ssa:deferstack() call for fn.deferstack. +func deferstackPreamble(fn *Function) (*Alloc, *Call) { + if alloc, _ := fn.vars[fn.deferstack].(*Alloc); alloc != nil { + for _, ref := range *alloc.Referrers() { + if ref, _ := ref.(*Store); ref != nil && ref.Addr == alloc { + if call, _ := ref.Val.(*Call); call != nil { + return alloc, call + } + } + } + } + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/go/ir/methods.go b/tools/vendor/honnef.co/go/tools/go/ir/methods.go index b7903c8472..f8607b03d0 100644 --- a/tools/vendor/honnef.co/go/tools/go/ir/methods.go +++ b/tools/vendor/honnef.co/go/tools/go/ir/methods.go @@ -27,7 +27,7 @@ func (prog *Program) MethodValue(sel *types.Selection) *Function { panic(fmt.Sprintf("MethodValue(%s) kind != MethodVal", sel)) } T := sel.Recv() - if isInterface(T) { + if types.IsInterface(T) { return nil // abstract method } if prog.mode&LogSource != 0 { @@ -165,7 +165,7 @@ func (prog *Program) needMethods(T types.Type, skip bool) { tmset := prog.MethodSets.MethodSet(T) - if !skip && !isInterface(T) && tmset.Len() > 0 { + if !skip && !types.IsInterface(T) && tmset.Len() > 0 { // Create methods of T. mset := prog.createMethodSet(T) if !mset.complete { @@ -235,6 +235,9 @@ func (prog *Program) needMethods(T types.Type, skip bool) { prog.needMethods(t.At(i).Type(), false) } + case *types.Alias: + prog.needMethods(types.Unalias(t), false) + default: lint.ExhaustiveTypeSwitch(T) } diff --git a/tools/vendor/honnef.co/go/tools/go/ir/print.go b/tools/vendor/honnef.co/go/tools/go/ir/print.go index ad23d16dcd..b30753dff2 100644 --- a/tools/vendor/honnef.co/go/tools/go/ir/print.go +++ b/tools/vendor/honnef.co/go/tools/go/ir/print.go @@ -14,6 +14,7 @@ import ( "io" "reflect" "sort" + "strings" "honnef.co/go/tools/go/types/typeutil" ) @@ -42,6 +43,14 @@ func relType(t types.Type, from *types.Package) string { return types.TypeString(t, types.RelativeTo(from)) } +func relTerm(term *types.Term, from *types.Package) string { + s := relType(term.Type(), from) + if term.Tilde() { + return "~" + s + } + return s +} + func relString(m Member, from *types.Package) string { // NB: not all globals have an Object (e.g. init$guard), // so use Package().Object not Object.Package(). @@ -398,7 +407,12 @@ func (recv *Recv) String() string { } func (s *Defer) String() string { - return printCall(&s.Call, "Defer", s) + prefix := "Defer " + if s._DeferStack != nil { + prefix += "[" + relName(s._DeferStack, s) + "] " + } + c := printCall(&s.Call, prefix, s) + return c } func (s *Select) String() string { @@ -505,3 +519,21 @@ func WritePackage(buf *bytes.Buffer, p *Package) { fmt.Fprintf(buf, "\n") } + +func (v *MultiConvert) String() string { + from := v.Parent().Pkg.Pkg + + var b strings.Builder + b.WriteString(printConv("MultiConvert", v, v.X)) + b.WriteString(" [") + for i, s := range v.from.Terms { + for j, d := range v.to.Terms { + if i != 0 || j != 0 { + b.WriteString(" | ") + } + fmt.Fprintf(&b, "%s -> %s", relTerm(s, from), relTerm(d, from)) + } + } + b.WriteString("]") + return b.String() +} diff --git a/tools/vendor/honnef.co/go/tools/go/ir/sanity.go b/tools/vendor/honnef.co/go/tools/go/ir/sanity.go index b6c59c95fc..4bbd711fae 100644 --- a/tools/vendor/honnef.co/go/tools/go/ir/sanity.go +++ b/tools/vendor/honnef.co/go/tools/go/ir/sanity.go @@ -8,7 +8,9 @@ package ir // Currently it checks CFG invariants but little at the instruction level. import ( + "bytes" "fmt" + "go/ast" "go/types" "io" "os" @@ -118,7 +120,7 @@ func (s *sanity) checkInstr(idx int, instr Instruction) { } else { for i, e := range instr.Edges { if e == nil { - s.errorf("phi node '%v' has no value for edge #%d from %s", instr, i, s.block.Preds[i]) + s.errorf("phi node '%s' has no value for edge #%d from %s", instr.Comment(), i, s.block.Preds[i]) } } } @@ -152,6 +154,7 @@ func (s *sanity) checkInstr(idx int, instr Instruction) { s.errorf("convert %s -> %s: at least one type set must contain basic type", instr.X.Type(), instr.Type()) } + case *MultiConvert: case *Defer: case *Extract: case *Field: @@ -371,7 +374,7 @@ func (s *sanity) checkBlock(b *BasicBlock, index int) { // Check that "untyped" types only appear on constant operands. if _, ok := (*op).(*Const); !ok { - if basic, ok := (*op).Type().(*types.Basic); ok { + if basic, ok := types.Unalias((*op).Type()).(*types.Basic); ok { if basic.Info()&types.IsUntyped != 0 { s.errorf("operand #%d of %s is untyped: %s", i, instr, basic) } @@ -442,8 +445,10 @@ func (s *sanity) checkFunction(fn *Function) bool { s.errorf("nil Prog") } + var buf bytes.Buffer _ = fn.String() // must not crash _ = fn.RelString(fn.pkg()) // must not crash + WriteFunction(&buf, fn) // must not crash // All functions have a package, except delegates (which are // shared across packages, or duplicated as weak symbols in a @@ -457,8 +462,11 @@ func (s *sanity) checkFunction(fn *Function) bool { } } } - if src, syn := fn.Synthetic == 0, fn.source != nil; src != syn { - s.errorf("got fromSource=%t, hasSyntax=%t; want same values", src, syn) + if syn, src := fn.Synthetic == 0, fn.source != nil; src != syn { + if _, ok := fn.source.(*ast.RangeStmt); !ok || fn.Synthetic != SyntheticRangeOverFuncYield { + // Only range-over-func yield functions are synthetic and have syntax + s.errorf("got fromSource=%t, hasSyntax=%t; want same values", src, syn) + } } for i, l := range fn.Locals { if l.Parent() != fn { diff --git a/tools/vendor/honnef.co/go/tools/go/ir/source.go b/tools/vendor/honnef.co/go/tools/go/ir/source.go index 155c5f7336..5e3ac28b81 100644 --- a/tools/vendor/honnef.co/go/tools/go/ir/source.go +++ b/tools/vendor/honnef.co/go/tools/go/ir/source.go @@ -181,13 +181,13 @@ func (prog *Program) ConstValue(obj *types.Const) *Const { // Universal constant? {true,false,nil} if obj.Parent() == types.Universe { - return NewConst(obj.Val(), obj.Type()) + return NewConst(obj.Val(), obj.Type(), nil) } // Package-level named constant? if v := prog.packageLevelValue(obj); v != nil { return v.(*Const) } - return NewConst(obj.Val(), obj.Type()) + return NewConst(obj.Val(), obj.Type(), nil) } // VarValue returns the IR Value that corresponds to a specific diff --git a/tools/vendor/honnef.co/go/tools/go/ir/ssa.go b/tools/vendor/honnef.co/go/tools/go/ir/ssa.go index 1ef87f9e88..6061b6085c 100644 --- a/tools/vendor/honnef.co/go/tools/go/ir/ssa.go +++ b/tools/vendor/honnef.co/go/tools/go/ir/ssa.go @@ -36,12 +36,10 @@ type Program struct { mode BuilderMode // set of mode bits for IR construction MethodSets typeutil.MethodSetCache // cache of type-checker's method-sets - methodsMu sync.Mutex // guards the following maps: - methodSets typeutil.Map[*methodSet] // maps type to its concrete methodSet - runtimeTypes typeutil.Map[bool] // types for which rtypes are needed - canon typeutil.Map[types.Type] // type canonicalization map - bounds map[*types.Func]*Function // bounds for curried x.Method closures - thunks map[selectionKey]*Function // thunks for T.Method expressions + methodsMu sync.Mutex // guards the following maps: + methodSets typeutil.Map[*methodSet] // maps type to its concrete methodSet + runtimeTypes typeutil.Map[bool] // types for which rtypes are needed + canon typeutil.Map[types.Type] // type canonicalization map } // A Package is a single analyzed Go package containing Members for @@ -64,10 +62,11 @@ type Package struct { // The following fields are set transiently, then cleared // after building. - buildOnce sync.Once // ensures package building occurs once - ninit int32 // number of init functions - info *types.Info // package type information - files []*ast.File // package ASTs + buildOnce sync.Once // ensures package building occurs once + ninit int32 // number of init functions + info *types.Info // package type information + files []*ast.File // package ASTs + initVersion map[ast.Expr]string // goversion to use for each global var init expr } // A Member is a member of a Go package, implemented by *NamedConst, @@ -292,6 +291,7 @@ const ( SyntheticWrapper SyntheticBound SyntheticGeneric + SyntheticRangeOverFuncYield ) func (syn Synthetic) String() string { @@ -308,6 +308,8 @@ func (syn Synthetic) String() string { return "bound" case SyntheticGeneric: return "generic" + case SyntheticRangeOverFuncYield: + return "range-over-func yield" default: return fmt.Sprintf("Synthetic(%d)", syn) } @@ -317,8 +319,8 @@ func (syn Synthetic) String() string { // or method. // // If Blocks is nil, this indicates an external function for which no -// Go source code is available. In this case, FreeVars and Locals -// are nil too. Clients performing whole-program analysis must +// Go source code is available. In this case, FreeVars, Locals, and +// Params are nil too. Clients performing whole-program analysis must // handle external functions specially. // // Blocks contains the function's control-flow graph (CFG). @@ -346,29 +348,51 @@ func (syn Synthetic) String() string { // the same position as the function they wrap. // Syntax.Pos() always returns the position of the declaring "func" token. // +// When the operand of a range statement is an iterator function, +// the loop body is transformed into a synthetic anonymous function +// that is passed as the yield argument in a call to the iterator. +// In that case, Function.Source() is the ast.RangeStmt. +// +// Synthetic functions, for which Synthetic != "", are functions +// that do not appear in the source AST. These include: +// - method wrappers, +// - thunks, +// - bound functions, +// - empty functions built from loaded type information, +// - yield functions created from range-over-func loops, and +// - package init functions. +// // Type() returns the function's Signature. type Function struct { node name string - object types.Object // a declared *types.Func or one of its wrappers + object *types.Func // symbol for declared function (nil for FuncLit or synthetic init) method *types.Selection // info about provenance of synthetic methods Signature *types.Signature generics instanceWrapperMap - Synthetic Synthetic - parent *Function // enclosing function if anon; nil if global - Pkg *Package // enclosing package; nil for shared funcs (wrappers and error.Error) - Prog *Program // enclosing program + Synthetic Synthetic // provenance of synthetic function; 0 for true source functions + parent *Function // enclosing function if anon; nil if global + Pkg *Package // enclosing package; nil for shared funcs (wrappers and error.Error) + Prog *Program // enclosing program + + // These fields are populated only when the function body is built: + Params []*Parameter // function parameters; for methods, includes receiver FreeVars []*FreeVar // free variables whose values must be supplied by closure - Locals []*Alloc // local variables of this function + Locals []*Alloc // frame-allocated variables of this function Blocks []*BasicBlock // basic blocks of the function; nil => external Exit *BasicBlock // The function's exit block - AnonFuncs []*Function // anonymous functions directly beneath this one + AnonFuncs []*Function // anonymous functions (from FuncLit, RangeStmt) directly beneath this one referrers []Instruction // referring instructions (iff Parent() != nil) NoReturn NoReturn // Calling this function will always terminate control flow. + goversion string // Go version of syntax (NB: init is special) + + // uniq is not stored in functionBody because we need it after function building finishes + uniq int64 // source of unique ints within the source tree while building + *functionBody } @@ -466,12 +490,16 @@ type constValue struct { type functionBody struct { // The following fields are set transiently during building, // then cleared. - currentBlock *BasicBlock // where to emit code - objects map[types.Object]Value // addresses of local variables - namedResults []*Alloc // tuple of named results - implicitResults []*Alloc // tuple of results - targets *targets // linked stack of branch targets - lblocks map[types.Object]*lblock // labelled blocks + currentBlock *BasicBlock // where to emit code + vars map[*types.Var]Value // addresses of local variables + results []*Alloc // result allocations of the current function + returnVars []*types.Var // variables for a return statement. Either results or for range-over-func a parent's results + targets *targets // linked stack of branch targets + lblocks map[*types.Label]*lblock // labelled blocks + jump *types.Var // synthetic variable for the yield state (non-nil => range-over-func) + deferstack *types.Var // synthetic variable holding enclosing ssa:deferstack() + sourceFn *Function // nearest enclosing source function + exits []*exit // exits of the function that need to be resolved consts map[constKey]constValue aggregateConsts typeutil.Map[[]*AggregateConst] @@ -486,13 +514,6 @@ type functionBody struct { scratchInstructions []Instruction } -func (fn *Function) results() []*Alloc { - if len(fn.namedResults) > 0 { - return fn.namedResults - } - return fn.implicitResults -} - // BasicBlock represents an IR basic block. // // The final element of Instrs is always an explicit transfer of @@ -560,7 +581,7 @@ type Parameter struct { register name string - object types.Object // a *types.Var; nil for non-source locals + object *types.Var // non-nil } // A Const represents the value of a constant expression. @@ -683,15 +704,12 @@ type Builtin struct { // type of the allocated variable is actually // Type().Underlying().(*types.Pointer).Elem(). // -// If Heap is false, Alloc allocates space in the function's -// activation record (frame); we refer to an Alloc(Heap=false) as a -// "stack" alloc. Each stack Alloc returns the same address each time -// it is executed within the same activation; the space is -// re-initialized to zero. +// If Heap is false, Alloc zero-initializes the same local variable in +// the call frame and returns its address; in this case the Alloc must +// be present in Function.Locals. We call this a "local" alloc. // -// If Heap is true, Alloc allocates space in the heap; we -// refer to an Alloc(Heap=true) as a "heap" alloc. Each heap Alloc -// returns a different address each time it is executed. +// If Heap is true, Alloc allocates a new zero-initialized variable +// each time the instruction is executed. We call this a "new" alloc. // // When Alloc is applied to a channel, map or slice type, it returns // the address of an uninitialized (nil) reference of that kind; store @@ -892,6 +910,26 @@ type Convert struct { X Value } +// The MultiConvert instruction yields the conversion of value X to type +// Type(). Either X.Type() or Type() must be a type parameter. Each +// type in the type set of X.Type() can be converted to each type in the +// type set of Type(). +// +// See the documentation for Convert, ChangeType, SliceToArray, and SliceToArrayPointer +// for the conversions that are permitted. +// +// This operation can fail dynamically (see SliceToArrayPointer). +// +// Example printed form: +// +// t1 = multiconvert D <- S (t0) [*[2]rune <- []rune | string <- []rune] +type MultiConvert struct { + register + X Value + from typeutil.TypeSet + to typeutil.TypeSet +} + // ChangeInterface constructs a value of one interface type from a // value of another interface type known to be assignable to it. // This operation cannot fail. @@ -1463,6 +1501,12 @@ type Go struct { // The Defer instruction pushes the specified call onto a stack of // functions to be called by a RunDefers instruction or by a panic. // +// If _DeferStack != nil, it indicates the defer list that the defer is +// added to. Defer list values come from the Builtin function +// ssa:deferstack. Calls to ssa:deferstack() produces the defer stack +// of the current function frame. _DeferStack allows for deferring into an +// alternative function stack than the current function. +// // See CallCommon for generic function call documentation. // // Pos() returns the ast.DeferStmt.Defer. @@ -1474,7 +1518,10 @@ type Go struct { // DeferInvoke t4.Bar t2 type Defer struct { anInstruction - Call CallCommon + Call CallCommon + _DeferStack Value // stack (from ssa:deferstack() intrinsic) onto which this function is pushed + + // TODO: Exporting _DeferStack and possibly making _DeferStack != nil awaits proposal https://github.com/golang/go/issues/66601. } // The Send instruction sends X on channel Chan. @@ -1790,13 +1837,18 @@ func (v *Global) String() string { return v.RelString(nil) func (v *Global) Package() *Package { return v.Pkg } func (v *Global) RelString(from *types.Package) string { return relString(v, from) } -func (v *Function) Name() string { return v.name } -func (v *Function) Type() types.Type { return v.Signature } -func (v *Function) Token() token.Token { return token.FUNC } -func (v *Function) Object() types.Object { return v.object } -func (v *Function) String() string { return v.RelString(nil) } -func (v *Function) Package() *Package { return v.Pkg } -func (v *Function) Parent() *Function { return v.parent } +func (v *Function) Name() string { return v.name } +func (v *Function) Type() types.Type { return v.Signature } +func (v *Function) Token() token.Token { return token.FUNC } +func (v *Function) Object() types.Object { + if v.object != nil { + return types.Object(v.object) + } + return nil +} +func (v *Function) String() string { return v.RelString(nil) } +func (v *Function) Package() *Package { return v.Pkg } +func (v *Function) Parent() *Function { return v.parent } func (v *Function) Referrers() *[]Instruction { if v.parent != nil { return &v.referrers @@ -1894,7 +1946,7 @@ func (s *Call) Operands(rands []*Value) []*Value { } func (s *Defer) Operands(rands []*Value) []*Value { - return s.Call.Operands(rands) + return append(s.Call.Operands(rands), &s._DeferStack) } func (v *ChangeInterface) Operands(rands []*Value) []*Value { @@ -1909,6 +1961,10 @@ func (v *Convert) Operands(rands []*Value) []*Value { return append(rands, &v.X) } +func (v *MultiConvert) Operands(rands []*Value) []*Value { + return append(rands, &v.X) +} + func (v *SliceToArrayPointer) Operands(rands []*Value) []*Value { return append(rands, &v.X) } diff --git a/tools/vendor/honnef.co/go/tools/go/ir/util.go b/tools/vendor/honnef.co/go/tools/go/ir/util.go index 0a733b654d..3a0e3ad92a 100644 --- a/tools/vendor/honnef.co/go/tools/go/ir/util.go +++ b/tools/vendor/honnef.co/go/tools/go/ir/util.go @@ -16,6 +16,8 @@ import ( "honnef.co/go/tools/go/ast/astutil" "honnef.co/go/tools/go/types/typeutil" + + "golang.org/x/exp/typeparams" ) //// AST utilities @@ -42,14 +44,15 @@ func isPointer(typ types.Type) bool { return ok } -func isInterface(T types.Type) bool { return types.IsInterface(T) } - // deref returns a pointer's element type; otherwise it returns typ. func deref(typ types.Type) types.Type { orig := typ + typ = types.Unalias(typ) if t, ok := typ.(*types.TypeParam); ok { if ctyp := typeutil.CoreType(t); ctyp != nil { + // This can happen, for example, with len(T) where T is a + // type parameter whose core type is a pointer to array. typ = ctyp } } @@ -146,3 +149,14 @@ func assert(x bool) { // BlockMap is a mapping from basic blocks (identified by their indices) to values. type BlockMap[T any] []T + +// isBasic reports whether t is a basic type. +func isBasic(t types.Type) bool { + _, ok := t.(*types.Basic) + return ok +} + +// isNonTypeParamInterface reports whether t is an interface type but not a type parameter. +func isNonTypeParamInterface(t types.Type) bool { + return !typeparams.IsTypeParam(t) && types.IsInterface(t) +} diff --git a/tools/vendor/honnef.co/go/tools/go/ir/wrappers.go b/tools/vendor/honnef.co/go/tools/go/ir/wrappers.go index 69537fb777..d5afb2cda3 100644 --- a/tools/vendor/honnef.co/go/tools/go/ir/wrappers.go +++ b/tools/vendor/honnef.co/go/tools/go/ir/wrappers.go @@ -93,8 +93,8 @@ func makeWrapper(prog *Program, sel *types.Selection) *Function { } c.Call.Args = []Value{ v, - emitConst(fn, stringConst(deref(sel.Recv()).String())), - emitConst(fn, stringConst(sel.Obj().Name())), + emitConst(fn, stringConst(deref(sel.Recv()).String(), nil)), + emitConst(fn, stringConst(sel.Obj().Name(), nil)), } c.setType(v.Type()) v = fn.emit(&c, nil) @@ -116,7 +116,7 @@ func makeWrapper(prog *Program, sel *types.Selection) *Function { // address of implicit C field. var c Call - if r := recvType(obj); !isInterface(r) { // concrete method + if r := recvType(obj); !types.IsInterface(r) { // concrete method if !isPointer(r) { v = emitLoad(fn, v, nil) } @@ -140,7 +140,7 @@ func makeWrapper(prog *Program, sel *types.Selection) *Function { func createParams(fn *Function, start int) { tparams := fn.Signature.Params() for i, n := start, tparams.Len(); i < n; i++ { - fn.addParamObj(tparams.At(i), nil) + fn.addParamVar(tparams.At(i), nil) } } @@ -173,42 +173,37 @@ func createParams(fn *Function, start int) { func makeBound(prog *Program, obj *types.Func) *Function { prog.methodsMu.Lock() defer prog.methodsMu.Unlock() - fn, ok := prog.bounds[obj] - if !ok { - if prog.mode&LogSource != 0 { - defer logStack("%s", SyntheticBound)() - } - fn = &Function{ - name: obj.Name() + "$bound", - object: obj, - Signature: changeRecv(obj.Type().(*types.Signature), nil), // drop receiver - Synthetic: SyntheticBound, - Prog: prog, - functionBody: new(functionBody), - } - fn.initHTML(prog.PrintFunc) - - fv := &FreeVar{name: "recv", typ: recvType(obj), parent: fn} - fn.FreeVars = []*FreeVar{fv} - fn.startBody() - createParams(fn, 0) - var c Call + if prog.mode&LogSource != 0 { + defer logStack("%s", SyntheticBound)() + } + fn := &Function{ + name: obj.Name() + "$bound", + object: obj, + Signature: changeRecv(obj.Type().(*types.Signature), nil), // drop receiver + Synthetic: SyntheticBound, + Prog: prog, + functionBody: new(functionBody), + } + fn.initHTML(prog.PrintFunc) - if !isInterface(recvType(obj)) { // concrete - c.Call.Value = prog.declaredFunc(obj) - c.Call.Args = []Value{fv} - } else { - c.Call.Value = fv - c.Call.Method = obj - } - for _, arg := range fn.Params { - c.Call.Args = append(c.Call.Args, arg) - } - emitTailCall(fn, &c, nil) - fn.finishBody() + fv := &FreeVar{name: "recv", typ: recvType(obj), parent: fn} + fn.FreeVars = []*FreeVar{fv} + fn.startBody() + createParams(fn, 0) + var c Call - prog.bounds[obj] = fn + if !types.IsInterface(recvType(obj)) { // concrete + c.Call.Value = prog.declaredFunc(obj) + c.Call.Args = []Value{fv} + } else { + c.Call.Value = fv + c.Call.Method = obj + } + for _, arg := range fn.Params { + c.Call.Args = append(c.Call.Args, arg) } + emitTailCall(fn, &c, nil) + fn.finishBody() return fn } @@ -231,42 +226,18 @@ func makeBound(prog *Program, obj *types.Func) *Function { // // f := func(t T) { return t.meth() } // -// TODO(adonovan): opt: currently the stub is created even when used -// directly in a function call: C.f(i, 0). This is less efficient -// than inlining the stub. -// // EXCLUSIVE_LOCKS_ACQUIRED(meth.Prog.methodsMu) func makeThunk(prog *Program, sel *types.Selection) *Function { if sel.Kind() != types.MethodExpr { panic(sel) } - key := selectionKey{ - kind: sel.Kind(), - recv: sel.Recv(), - obj: sel.Obj(), - index: fmt.Sprint(sel.Index()), - indirect: sel.Indirect(), - } - prog.methodsMu.Lock() defer prog.methodsMu.Unlock() - // Canonicalize key.recv to avoid constructing duplicate thunks. - canonRecv, ok := prog.canon.At(key.recv) - if !ok { - canonRecv = key.recv - prog.canon.Set(key.recv, canonRecv) - } - key.recv = canonRecv - - fn, ok := prog.thunks[key] - if !ok { - fn = makeWrapper(prog, sel) - if fn.Signature.Recv() != nil { - panic(fn) // unexpected receiver - } - prog.thunks[key] = fn + fn := makeWrapper(prog, sel) + if fn.Signature.Recv() != nil { + panic(fn) // unexpected receiver } return fn } @@ -275,15 +246,6 @@ func changeRecv(s *types.Signature, recv *types.Var) *types.Signature { return types.NewSignatureType(recv, nil, nil, s.Params(), s.Results(), s.Variadic()) } -// selectionKey is like types.Selection but a usable map key. -type selectionKey struct { - kind types.SelectionKind - recv types.Type // canonicalized via Program.canon - obj types.Object - index string - indirect bool -} - // makeInstance creates a wrapper function with signature sig that calls the generic function fn. // If targs is not nil, fn is a function and targs describes the concrete type arguments. // If targs is nil, fn is a method and the type arguments are derived from the receiver. @@ -291,7 +253,7 @@ func makeInstance(prog *Program, fn *Function, sig *types.Signature, targs *type if sig.Recv() != nil { assert(targs == nil) // Methods don't have their own type parameters, but the receiver does - targs = deref(sig.Recv().Type()).(*types.Named).TypeArgs() + targs = types.Unalias(deref(sig.Recv().Type())).(*types.Named).TypeArgs() } else { assert(targs != nil) } @@ -318,7 +280,7 @@ func makeInstance(prog *Program, fn *Function, sig *types.Signature, targs *type w.initHTML(prog.PrintFunc) w.startBody() if sig.Recv() != nil { - w.addParamObj(sig.Recv(), nil) + w.addParamVar(sig.Recv(), nil) } createParams(w, 0) var c Call diff --git a/tools/vendor/honnef.co/go/tools/go/types/typeutil/ext.go b/tools/vendor/honnef.co/go/tools/go/types/typeutil/ext.go index bc553ec01c..19b4ae5a7e 100644 --- a/tools/vendor/honnef.co/go/tools/go/types/typeutil/ext.go +++ b/tools/vendor/honnef.co/go/tools/go/types/typeutil/ext.go @@ -16,3 +16,12 @@ func (t *Iterator) Elem() types.Type { return t.elem } func NewIterator(elem types.Type) *Iterator { return &Iterator{elem: elem} } + +type DeferStack struct{} + +func (t *DeferStack) Underlying() types.Type { return t } +func (t *DeferStack) String() string { return "deferStack" } + +func NewDeferStack() *DeferStack { + return &DeferStack{} +} diff --git a/tools/vendor/honnef.co/go/tools/go/types/typeutil/util.go b/tools/vendor/honnef.co/go/tools/go/types/typeutil/util.go index 3a2ad973bb..ef11564c79 100644 --- a/tools/vendor/honnef.co/go/tools/go/types/typeutil/util.go +++ b/tools/vendor/honnef.co/go/tools/go/types/typeutil/util.go @@ -3,7 +3,10 @@ package typeutil import ( "bytes" "go/types" + "strings" "sync" + + "golang.org/x/exp/typeparams" ) var bufferPool = &sync.Pool{ @@ -15,6 +18,11 @@ var bufferPool = &sync.Pool{ } func FuncName(f *types.Func) string { + // We don't care about aliases in this function because we use FuncName to check calls + // to known methods, and method receivers are determined by the method declaration, + // not the call. Thus, even if a user does 'type Alias = *sync.Mutex' and calls + // Alias.Lock, we'll still see it as (*sync.Mutex).Lock. + buf := bufferPool.Get().(*bytes.Buffer) buf.Reset() if f.Type() != nil { @@ -80,12 +88,82 @@ func IsObject(obj types.Object, name string) bool { return path+obj.Name() == name } -// OPT(dh): IsType is kind of expensive; should we really use it? -func IsType(T types.Type, name string) bool { return types.TypeString(T, nil) == name } +// IsTypeName reports whether obj represents the qualified name. If obj is a type alias, +// IsTypeName checks both the alias and the aliased type, if the aliased type has a type +// name. +func IsTypeName(obj *types.TypeName, name string) bool { + var qf string + if idx := strings.LastIndex(name, "."); idx != -1 { + qf = name[:idx] + name = name[idx+1:] + } + if obj.Name() == name && + ((qf == "" && obj.Pkg() == nil) || (obj.Pkg() != nil && obj.Pkg().Path() == qf)) { + return true + } + + if !obj.IsAlias() { + return false + } + + // FIXME(dh): we should peel away one layer of alias at a time; this is blocked on + // github.com/golang/go/issues/66559 + if typ, ok := types.Unalias(obj.Type()).(interface{ Obj() *types.TypeName }); ok { + return IsTypeName(typ.Obj(), name) + } + + return false +} + +func IsPointerToTypeWithName(typ types.Type, name string) bool { + ptr, ok := types.Unalias(typ).(*types.Pointer) + if !ok { + return false + } + return IsTypeWithName(ptr.Elem(), name) +} + +// IsTypeWithName reports whether typ represents a type with the qualified name, If typ is +// a type alias, IsTypeWithName checks both the alias and the aliased type. The following +// types can have names: Basic, Named, Alias. +func IsTypeWithName(typ types.Type, name string) bool { + switch typ := typ.(type) { + case *types.Basic: + return typ.Name() == name + case *types.Named: + return IsTypeName(typ.Obj(), name) + case *types.Alias: + // FIXME(dh): we should peel away one layer of alias at a time; this is blocked on + // github.com/golang/go/issues/66559 + + // IsTypeName already handles aliases to other aliases or named types; our + // fallback is required for aliases to basic types. + return IsTypeName(typ.Obj(), name) || IsTypeWithName(types.Unalias(typ), name) + default: + return false + } +} +// IsPointerLike returns true if type T is like a pointer. This returns true for all nillable types, +// unsafe.Pointer, and type sets where at least one term is pointer-like. func IsPointerLike(T types.Type) bool { switch T := T.Underlying().(type) { - case *types.Interface, *types.Chan, *types.Map, *types.Signature, *types.Pointer, *types.Slice: + case *types.Interface: + if T.IsMethodSet() { + return true + } else { + terms, err := typeparams.NormalTerms(T) + if err != nil { + return false + } + for _, term := range terms { + if IsPointerLike(term.Type()) { + return true + } + } + return false + } + case *types.Chan, *types.Map, *types.Signature, *types.Pointer, *types.Slice: return true case *types.Basic: return T.Kind() == types.UnsafePointer diff --git a/tools/vendor/honnef.co/go/tools/internal/passes/buildir/buildir.go b/tools/vendor/honnef.co/go/tools/internal/passes/buildir/buildir.go index 51dfaef531..dc26a67219 100644 --- a/tools/vendor/honnef.co/go/tools/internal/passes/buildir/buildir.go +++ b/tools/vendor/honnef.co/go/tools/internal/passes/buildir/buildir.go @@ -35,7 +35,7 @@ var Analyzer = &analysis.Analyzer{ } // IR provides intermediate representation for all the -// non-blank source functions in the current package. +// source functions in the current package. type IR struct { Pkg *ir.Package SrcFuncs []*ir.Function diff --git a/tools/vendor/honnef.co/go/tools/internal/sharedcheck/lint.go b/tools/vendor/honnef.co/go/tools/internal/sharedcheck/lint.go index 6e4cdff26a..b78899ab54 100644 --- a/tools/vendor/honnef.co/go/tools/internal/sharedcheck/lint.go +++ b/tools/vendor/honnef.co/go/tools/internal/sharedcheck/lint.go @@ -44,7 +44,7 @@ func CheckRangeStringRunes(pass *analysis.Pass) (interface{}, error) { if !ok { return true } - TdstElem, ok := Tdst.Elem().(*types.Basic) + TdstElem, ok := types.Unalias(Tdst.Elem()).(*types.Basic) if !ok || TdstElem.Kind() != types.Int32 { return true } @@ -154,7 +154,7 @@ func RedundantTypeInDeclarationChecker(verb string, flagHelpfulTypes bool) *anal if err != nil { panic(err) } - if b, ok := tv.Type.(*types.Basic); ok && (b.Info()&types.IsUntyped) != 0 { + if b, ok := types.Unalias(tv.Type).(*types.Basic); ok && (b.Info()&types.IsUntyped) != 0 { if Tlhs != types.Default(b) { // The rhs is untyped and its default type differs from the explicit type on the lhs continue specLoop diff --git a/tools/vendor/honnef.co/go/tools/knowledge/arg.go b/tools/vendor/honnef.co/go/tools/knowledge/arg.go index 1fe61c1d57..4fab2eba19 100644 --- a/tools/vendor/honnef.co/go/tools/knowledge/arg.go +++ b/tools/vendor/honnef.co/go/tools/knowledge/arg.go @@ -1,58 +1,66 @@ package knowledge var Args = map[string]int{ - "(*encoding/json.Decoder).Decode.v": 0, - "(*encoding/json.Encoder).Encode.v": 0, - "(*encoding/xml.Decoder).Decode.v": 0, - "(*encoding/xml.Encoder).Encode.v": 0, - "(*sync.Pool).Put.x": 0, - "(*text/template.Template).Parse.text": 0, - "(io.Seeker).Seek.offset": 0, - "(time.Time).Sub.u": 0, - "append.elems": 1, - "append.slice": 0, - "bytes.Equal.a": 0, - "bytes.Equal.b": 1, - "encoding/binary.Write.data": 2, - "errors.New.text": 0, - "fmt.Fprintf.format": 1, - "fmt.Printf.format": 0, - "fmt.Sprintf.a[0]": 1, - "fmt.Sprintf.format": 0, - "json.Marshal.v": 0, - "json.Unmarshal.v": 1, - "len.v": 0, - "make.size[0]": 1, - "make.size[1]": 2, - "make.t": 0, - "net/url.Parse.rawurl": 0, - "os.OpenFile.flag": 1, - "os/exec.Command.name": 0, - "os/signal.Notify.c": 0, - "regexp.Compile.expr": 0, - "runtime.SetFinalizer.finalizer": 1, - "runtime.SetFinalizer.obj": 0, - "sort.Sort.data": 0, - "strconv.AppendFloat.bitSize": 4, - "strconv.AppendFloat.fmt": 2, - "strconv.AppendInt.base": 2, - "strconv.AppendUint.base": 2, - "strconv.FormatComplex.bitSize": 3, - "strconv.FormatComplex.fmt": 1, - "strconv.FormatFloat.bitSize": 3, - "strconv.FormatFloat.fmt": 1, - "strconv.FormatInt.base": 1, - "strconv.FormatUint.base": 1, - "strconv.ParseComplex.bitSize": 1, - "strconv.ParseFloat.bitSize": 1, - "strconv.ParseInt.base": 1, - "strconv.ParseInt.bitSize": 2, - "strconv.ParseUint.base": 1, - "strconv.ParseUint.bitSize": 2, - "time.Parse.layout": 0, - "time.Sleep.d": 0, - "xml.Marshal.v": 0, - "xml.Unmarshal.v": 1, + "(*sync.Pool).Put.x": 0, + "(*text/template.Template).Parse.text": 0, + "(io.Seeker).Seek.offset": 0, + "(time.Time).Sub.u": 0, + "append.elems": 1, + "append.slice": 0, + "bytes.Equal.a": 0, + "bytes.Equal.b": 1, + "encoding/ascii85.Encode.dst": 0, + "encoding/ascii85.Encode.src": 1, + "(*encoding/base32.Encoding).Encode.dst": 0, + "(*encoding/base32.Encoding).Encode.src": 1, + "(*encoding/base64.Encoding).Encode.dst": 0, + "(*encoding/base64.Encoding).Encode.src": 1, + "encoding/binary.Write.data": 2, + "encoding/hex.Encode.dst": 0, + "encoding/hex.Encode.src": 1, + "(*encoding/json.Decoder).Decode.v": 0, + "(*encoding/json.Encoder).Encode.v": 0, + "(*encoding/xml.Decoder).Decode.v": 0, + "(*encoding/xml.Encoder).Encode.v": 0, + "errors.New.text": 0, + "fmt.Fprintf.format": 1, + "fmt.Printf.format": 0, + "fmt.Sprintf.a[0]": 1, + "fmt.Sprintf.format": 0, + "json.Marshal.v": 0, + "json.Unmarshal.v": 1, + "len.v": 0, + "make.size[0]": 1, + "make.size[1]": 2, + "make.t": 0, + "net/url.Parse.rawurl": 0, + "os.OpenFile.flag": 1, + "os/exec.Command.name": 0, + "os/signal.Notify.c": 0, + "regexp.Compile.expr": 0, + "runtime.SetFinalizer.finalizer": 1, + "runtime.SetFinalizer.obj": 0, + "sort.Sort.data": 0, + "strconv.AppendFloat.bitSize": 4, + "strconv.AppendFloat.fmt": 2, + "strconv.AppendInt.base": 2, + "strconv.AppendUint.base": 2, + "strconv.FormatComplex.bitSize": 3, + "strconv.FormatComplex.fmt": 1, + "strconv.FormatFloat.bitSize": 3, + "strconv.FormatFloat.fmt": 1, + "strconv.FormatInt.base": 1, + "strconv.FormatUint.base": 1, + "strconv.ParseComplex.bitSize": 1, + "strconv.ParseFloat.bitSize": 1, + "strconv.ParseInt.base": 1, + "strconv.ParseInt.bitSize": 2, + "strconv.ParseUint.base": 1, + "strconv.ParseUint.bitSize": 2, + "time.Parse.layout": 0, + "time.Sleep.d": 0, + "xml.Marshal.v": 0, + "xml.Unmarshal.v": 1, } // Arg turns the name of an argument into an argument index. diff --git a/tools/vendor/honnef.co/go/tools/knowledge/deprecated.go b/tools/vendor/honnef.co/go/tools/knowledge/deprecated.go index caeb49975c..854e5bd1fb 100644 --- a/tools/vendor/honnef.co/go/tools/knowledge/deprecated.go +++ b/tools/vendor/honnef.co/go/tools/knowledge/deprecated.go @@ -2,18 +2,18 @@ package knowledge const ( // DeprecatedNeverUse indicates that an API should never be used, regardless of Go version. - DeprecatedNeverUse = -1 + DeprecatedNeverUse = "never" // DeprecatedUseNoLonger indicates that an API has no use anymore. - DeprecatedUseNoLonger = -2 + DeprecatedUseNoLonger = "no longer" ) // Deprecation describes when a Go API has been deprecated. type Deprecation struct { // The minor Go version since which this API has been deprecated. - DeprecatedSince int + DeprecatedSince string // The minor Go version since which an alternative API has been available. // May also be one of DeprecatedNeverUse or DeprecatedUseNoLonger. - AlternativeAvailableSince int + AlternativeAvailableSince string } // go/importer.ForCompiler contains "Deprecated:", but it refers to a single argument, not the whole function. @@ -27,246 +27,188 @@ type Deprecation struct { var StdlibDeprecations = map[string]Deprecation{ // FIXME(dh): AllowBinary isn't being detected as deprecated // because the comment has a newline right after "Deprecated:" - "go/build.AllowBinary": {7, 7}, - "(archive/zip.FileHeader).CompressedSize": {1, 1}, - "(archive/zip.FileHeader).UncompressedSize": {1, 1}, - "(archive/zip.FileHeader).ModifiedTime": {10, 10}, - "(archive/zip.FileHeader).ModifiedDate": {10, 10}, - "(*archive/zip.FileHeader).ModTime": {10, 10}, - "(*archive/zip.FileHeader).SetModTime": {10, 10}, - "(go/doc.Package).Bugs": {1, 1}, - "os.SEEK_SET": {7, 7}, - "os.SEEK_CUR": {7, 7}, - "os.SEEK_END": {7, 7}, - "(net.Dialer).Cancel": {7, 7}, - "runtime.CPUProfile": {9, 0}, - "compress/flate.ReadError": {6, DeprecatedUseNoLonger}, - "compress/flate.WriteError": {6, DeprecatedUseNoLonger}, - "path/filepath.HasPrefix": {0, DeprecatedNeverUse}, - "(net/http.Transport).Dial": {7, 7}, - "(net/http.Transport).DialTLS": {14, 14}, - "(*net/http.Transport).CancelRequest": {6, 5}, - "net/http.ErrWriteAfterFlush": {7, DeprecatedUseNoLonger}, - "net/http.ErrHeaderTooLong": {8, DeprecatedUseNoLonger}, - "net/http.ErrShortBody": {8, DeprecatedUseNoLonger}, - "net/http.ErrMissingContentLength": {8, DeprecatedUseNoLonger}, - "net/http/httputil.ErrPersistEOF": {0, DeprecatedUseNoLonger}, - "net/http/httputil.ErrClosed": {0, DeprecatedUseNoLonger}, - "net/http/httputil.ErrPipeline": {0, DeprecatedUseNoLonger}, - "net/http/httputil.ServerConn": {0, 0}, - "net/http/httputil.NewServerConn": {0, 0}, - "net/http/httputil.ClientConn": {0, 0}, - "net/http/httputil.NewClientConn": {0, 0}, - "net/http/httputil.NewProxyClientConn": {0, 0}, - "(net/http.Request).Cancel": {7, 7}, - "(text/template/parse.PipeNode).Line": {1, DeprecatedUseNoLonger}, - "(text/template/parse.ActionNode).Line": {1, DeprecatedUseNoLonger}, - "(text/template/parse.BranchNode).Line": {1, DeprecatedUseNoLonger}, - "(text/template/parse.TemplateNode).Line": {1, DeprecatedUseNoLonger}, - "database/sql/driver.ColumnConverter": {9, 9}, - "database/sql/driver.Execer": {8, 8}, - "database/sql/driver.Queryer": {8, 8}, - "(database/sql/driver.Conn).Begin": {8, 8}, - "(database/sql/driver.Stmt).Exec": {8, 8}, - "(database/sql/driver.Stmt).Query": {8, 8}, - "syscall.StringByteSlice": {1, 1}, - "syscall.StringBytePtr": {1, 1}, - "syscall.StringSlicePtr": {1, 1}, - "syscall.StringToUTF16": {1, 1}, - "syscall.StringToUTF16Ptr": {1, 1}, - "(*regexp.Regexp).Copy": {12, DeprecatedUseNoLonger}, - "(archive/tar.Header).Xattrs": {10, 10}, - "archive/tar.TypeRegA": {11, 1}, - "go/types.NewInterface": {11, 11}, - "(*go/types.Interface).Embedded": {11, 11}, - "go/importer.For": {12, 12}, - "encoding/json.InvalidUTF8Error": {2, DeprecatedUseNoLonger}, - "encoding/json.UnmarshalFieldError": {2, DeprecatedUseNoLonger}, - "encoding/csv.ErrTrailingComma": {2, DeprecatedUseNoLonger}, - "(encoding/csv.Reader).TrailingComma": {2, DeprecatedUseNoLonger}, - "(net.Dialer).DualStack": {12, 12}, - "net/http.ErrUnexpectedTrailer": {12, DeprecatedUseNoLonger}, - "net/http.CloseNotifier": {11, 7}, + "go/build.AllowBinary": {"go1.7", "go1.7"}, + "(archive/zip.FileHeader).CompressedSize": {"go1.1", "go1.1"}, + "(archive/zip.FileHeader).UncompressedSize": {"go1.1", "go1.1"}, + "(archive/zip.FileHeader).ModifiedTime": {"go1.10", "go1.10"}, + "(archive/zip.FileHeader).ModifiedDate": {"go1.10", "go1.10"}, + "(*archive/zip.FileHeader).ModTime": {"go1.10", "go1.10"}, + "(*archive/zip.FileHeader).SetModTime": {"go1.10", "go1.10"}, + "(go/doc.Package).Bugs": {"go1.1", "go1.1"}, + "os.SEEK_SET": {"go1.7", "go1.7"}, + "os.SEEK_CUR": {"go1.7", "go1.7"}, + "os.SEEK_END": {"go1.7", "go1.7"}, + "(net.Dialer).Cancel": {"go1.7", "go1.7"}, + "runtime.CPUProfile": {"go1.9", "go1.0"}, + "compress/flate.ReadError": {"go1.6", DeprecatedUseNoLonger}, + "compress/flate.WriteError": {"go1.6", DeprecatedUseNoLonger}, + "path/filepath.HasPrefix": {"go1.0", DeprecatedNeverUse}, + "(net/http.Transport).Dial": {"go1.7", "go1.7"}, + "(net/http.Transport).DialTLS": {"go1.14", "go1.14"}, + "(*net/http.Transport).CancelRequest": {"go1.6", "go1.5"}, + "net/http.ErrWriteAfterFlush": {"go1.7", DeprecatedUseNoLonger}, + "net/http.ErrHeaderTooLong": {"go1.8", DeprecatedUseNoLonger}, + "net/http.ErrShortBody": {"go1.8", DeprecatedUseNoLonger}, + "net/http.ErrMissingContentLength": {"go1.8", DeprecatedUseNoLonger}, + "net/http/httputil.ErrPersistEOF": {"go1.0", DeprecatedUseNoLonger}, + "net/http/httputil.ErrClosed": {"go1.0", DeprecatedUseNoLonger}, + "net/http/httputil.ErrPipeline": {"go1.0", DeprecatedUseNoLonger}, + "net/http/httputil.ServerConn": {"go1.0", "go1.0"}, + "net/http/httputil.NewServerConn": {"go1.0", "go1.0"}, + "net/http/httputil.ClientConn": {"go1.0", "go1.0"}, + "net/http/httputil.NewClientConn": {"go1.0", "go1.0"}, + "net/http/httputil.NewProxyClientConn": {"go1.0", "go1.0"}, + "(net/http.Request).Cancel": {"go1.7", "go1.7"}, + "(text/template/parse.PipeNode).Line": {"go1.1", DeprecatedUseNoLonger}, + "(text/template/parse.ActionNode).Line": {"go1.1", DeprecatedUseNoLonger}, + "(text/template/parse.BranchNode).Line": {"go1.1", DeprecatedUseNoLonger}, + "(text/template/parse.TemplateNode).Line": {"go1.1", DeprecatedUseNoLonger}, + "database/sql/driver.ColumnConverter": {"go1.9", "go1.9"}, + "database/sql/driver.Execer": {"go1.8", "go1.8"}, + "database/sql/driver.Queryer": {"go1.8", "go1.8"}, + "(database/sql/driver.Conn).Begin": {"go1.8", "go1.8"}, + "(database/sql/driver.Stmt).Exec": {"go1.8", "go1.8"}, + "(database/sql/driver.Stmt).Query": {"go1.8", "go1.8"}, + "syscall.StringByteSlice": {"go1.1", "go1.1"}, + "syscall.StringBytePtr": {"go1.1", "go1.1"}, + "syscall.StringSlicePtr": {"go1.1", "go1.1"}, + "syscall.StringToUTF16": {"go1.1", "go1.1"}, + "syscall.StringToUTF16Ptr": {"go1.1", "go1.1"}, + "(*regexp.Regexp).Copy": {"go1.12", DeprecatedUseNoLonger}, + "(archive/tar.Header).Xattrs": {"go1.10", "go1.10"}, + "archive/tar.TypeRegA": {"go1.11", "go1.1"}, + "go/types.NewInterface": {"go1.11", "go1.11"}, + "(*go/types.Interface).Embedded": {"go1.11", "go1.11"}, + "go/importer.For": {"go1.12", "go1.12"}, + "encoding/json.InvalidUTF8Error": {"go1.2", DeprecatedUseNoLonger}, + "encoding/json.UnmarshalFieldError": {"go1.2", DeprecatedUseNoLonger}, + "encoding/csv.ErrTrailingComma": {"go1.2", DeprecatedUseNoLonger}, + "(encoding/csv.Reader).TrailingComma": {"go1.2", DeprecatedUseNoLonger}, + "(net.Dialer).DualStack": {"go1.12", "go1.12"}, + "net/http.ErrUnexpectedTrailer": {"go1.12", DeprecatedUseNoLonger}, + "net/http.CloseNotifier": {"go1.11", "go1.7"}, // This is hairy. The notice says "Not all errors in the http package related to protocol errors are of type ProtocolError", but doesn't that imply that some errors do? - "net/http.ProtocolError": {8, DeprecatedUseNoLonger}, - "(crypto/x509.CertificateRequest).Attributes": {5, 3}, - "(*crypto/x509.Certificate).CheckCRLSignature": {19, 19}, - "crypto/x509.ParseCRL": {19, 19}, - "crypto/x509.ParseDERCRL": {19, 19}, - "(*crypto/x509.Certificate).CreateCRL": {19, 19}, - "crypto/x509/pkix.TBSCertificateList": {19, 19}, - "crypto/x509/pkix.RevokedCertificate": {19, 19}, - "go/doc.ToHTML": {20, 20}, - "go/doc.ToText": {20, 20}, - "go/doc.Synopsis": {20, 20}, - "math/rand.Seed": {20, 0}, - "math/rand.Read": {20, DeprecatedNeverUse}, + "net/http.ProtocolError": {"go1.8", DeprecatedUseNoLonger}, + "(crypto/x509.CertificateRequest).Attributes": {"go1.5", "go1.3"}, + "(*crypto/x509.Certificate).CheckCRLSignature": {"go1.19", "go1.19"}, + "crypto/x509.ParseCRL": {"go1.19", "go1.19"}, + "crypto/x509.ParseDERCRL": {"go1.19", "go1.19"}, + "(*crypto/x509.Certificate).CreateCRL": {"go1.19", "go1.19"}, + "crypto/x509/pkix.TBSCertificateList": {"go1.19", "go1.19"}, + "crypto/x509/pkix.RevokedCertificate": {"go1.19", "go1.19"}, + "go/doc.ToHTML": {"go1.20", "go1.20"}, + "go/doc.ToText": {"go1.20", "go1.20"}, + "go/doc.Synopsis": {"go1.20", "go1.20"}, + "math/rand.Seed": {"go1.20", "go1.0"}, + "math/rand.Read": {"go1.20", DeprecatedNeverUse}, // These functions have no direct alternative, but they are insecure and should no longer be used. - "crypto/x509.IsEncryptedPEMBlock": {16, DeprecatedNeverUse}, - "crypto/x509.DecryptPEMBlock": {16, DeprecatedNeverUse}, - "crypto/x509.EncryptPEMBlock": {16, DeprecatedNeverUse}, - "crypto/dsa": {16, DeprecatedNeverUse}, + "crypto/x509.IsEncryptedPEMBlock": {"go1.16", DeprecatedNeverUse}, + "crypto/x509.DecryptPEMBlock": {"go1.16", DeprecatedNeverUse}, + "crypto/x509.EncryptPEMBlock": {"go1.16", DeprecatedNeverUse}, + "crypto/dsa": {"go1.16", DeprecatedNeverUse}, // This function has no alternative, but also no purpose. - "(*crypto/rc4.Cipher).Reset": {12, DeprecatedNeverUse}, - "(net/http/httptest.ResponseRecorder).HeaderMap": {11, 7}, - "image.ZP": {13, 0}, - "image.ZR": {13, 0}, - "(*debug/gosym.LineTable).LineToPC": {2, 2}, - "(*debug/gosym.LineTable).PCToLine": {2, 2}, - "crypto/tls.VersionSSL30": {13, DeprecatedNeverUse}, - "(crypto/tls.Config).NameToCertificate": {14, DeprecatedUseNoLonger}, - "(*crypto/tls.Config).BuildNameToCertificate": {14, DeprecatedUseNoLonger}, - "(crypto/tls.Config).SessionTicketKey": {16, 5}, + "(*crypto/rc4.Cipher).Reset": {"go1.12", DeprecatedNeverUse}, + "(net/http/httptest.ResponseRecorder).HeaderMap": {"go1.11", "go1.7"}, + "image.ZP": {"go1.13", "go1.0"}, + "image.ZR": {"go1.13", "go1.0"}, + "(*debug/gosym.LineTable).LineToPC": {"go1.2", "go1.2"}, + "(*debug/gosym.LineTable).PCToLine": {"go1.2", "go1.2"}, + "crypto/tls.VersionSSL30": {"go1.13", DeprecatedNeverUse}, + "(crypto/tls.Config).NameToCertificate": {"go1.14", DeprecatedUseNoLonger}, + "(*crypto/tls.Config).BuildNameToCertificate": {"go1.14", DeprecatedUseNoLonger}, + "(crypto/tls.Config).SessionTicketKey": {"go1.16", "go1.5"}, // No alternative, no use - "(crypto/tls.ConnectionState).NegotiatedProtocolIsMutual": {16, DeprecatedNeverUse}, + "(crypto/tls.ConnectionState).NegotiatedProtocolIsMutual": {"go1.16", DeprecatedNeverUse}, // No alternative, but insecure - "(crypto/tls.ConnectionState).TLSUnique": {16, DeprecatedNeverUse}, - "image/jpeg.Reader": {4, DeprecatedNeverUse}, + "(crypto/tls.ConnectionState).TLSUnique": {"go1.16", DeprecatedNeverUse}, + "image/jpeg.Reader": {"go1.4", DeprecatedNeverUse}, // All of these have been deprecated in favour of external libraries - "syscall.AttachLsf": {7, 0}, - "syscall.DetachLsf": {7, 0}, - "syscall.LsfSocket": {7, 0}, - "syscall.SetLsfPromisc": {7, 0}, - "syscall.LsfJump": {7, 0}, - "syscall.LsfStmt": {7, 0}, - "syscall.BpfStmt": {7, 0}, - "syscall.BpfJump": {7, 0}, - "syscall.BpfBuflen": {7, 0}, - "syscall.SetBpfBuflen": {7, 0}, - "syscall.BpfDatalink": {7, 0}, - "syscall.SetBpfDatalink": {7, 0}, - "syscall.SetBpfPromisc": {7, 0}, - "syscall.FlushBpf": {7, 0}, - "syscall.BpfInterface": {7, 0}, - "syscall.SetBpfInterface": {7, 0}, - "syscall.BpfTimeout": {7, 0}, - "syscall.SetBpfTimeout": {7, 0}, - "syscall.BpfStats": {7, 0}, - "syscall.SetBpfImmediate": {7, 0}, - "syscall.SetBpf": {7, 0}, - "syscall.CheckBpfVersion": {7, 0}, - "syscall.BpfHeadercmpl": {7, 0}, - "syscall.SetBpfHeadercmpl": {7, 0}, - "syscall.RouteRIB": {8, 0}, - "syscall.RoutingMessage": {8, 0}, - "syscall.RouteMessage": {8, 0}, - "syscall.InterfaceMessage": {8, 0}, - "syscall.InterfaceAddrMessage": {8, 0}, - "syscall.ParseRoutingMessage": {8, 0}, - "syscall.ParseRoutingSockaddr": {8, 0}, - "syscall.InterfaceAnnounceMessage": {7, 0}, - "syscall.InterfaceMulticastAddrMessage": {7, 0}, - "syscall.FormatMessage": {5, 0}, - "syscall.PostQueuedCompletionStatus": {17, 0}, - "syscall.GetQueuedCompletionStatus": {17, 0}, - "syscall.CreateIoCompletionPort": {17, 0}, + "syscall.AttachLsf": {"go1.7", "go1.0"}, + "syscall.DetachLsf": {"go1.7", "go1.0"}, + "syscall.LsfSocket": {"go1.7", "go1.0"}, + "syscall.SetLsfPromisc": {"go1.7", "go1.0"}, + "syscall.LsfJump": {"go1.7", "go1.0"}, + "syscall.LsfStmt": {"go1.7", "go1.0"}, + "syscall.BpfStmt": {"go1.7", "go1.0"}, + "syscall.BpfJump": {"go1.7", "go1.0"}, + "syscall.BpfBuflen": {"go1.7", "go1.0"}, + "syscall.SetBpfBuflen": {"go1.7", "go1.0"}, + "syscall.BpfDatalink": {"go1.7", "go1.0"}, + "syscall.SetBpfDatalink": {"go1.7", "go1.0"}, + "syscall.SetBpfPromisc": {"go1.7", "go1.0"}, + "syscall.FlushBpf": {"go1.7", "go1.0"}, + "syscall.BpfInterface": {"go1.7", "go1.0"}, + "syscall.SetBpfInterface": {"go1.7", "go1.0"}, + "syscall.BpfTimeout": {"go1.7", "go1.0"}, + "syscall.SetBpfTimeout": {"go1.7", "go1.0"}, + "syscall.BpfStats": {"go1.7", "go1.0"}, + "syscall.SetBpfImmediate": {"go1.7", "go1.0"}, + "syscall.SetBpf": {"go1.7", "go1.0"}, + "syscall.CheckBpfVersion": {"go1.7", "go1.0"}, + "syscall.BpfHeadercmpl": {"go1.7", "go1.0"}, + "syscall.SetBpfHeadercmpl": {"go1.7", "go1.0"}, + "syscall.RouteRIB": {"go1.8", "go1.0"}, + "syscall.RoutingMessage": {"go1.8", "go1.0"}, + "syscall.RouteMessage": {"go1.8", "go1.0"}, + "syscall.InterfaceMessage": {"go1.8", "go1.0"}, + "syscall.InterfaceAddrMessage": {"go1.8", "go1.0"}, + "syscall.ParseRoutingMessage": {"go1.8", "go1.0"}, + "syscall.ParseRoutingSockaddr": {"go1.8", "go1.0"}, + "syscall.InterfaceAnnounceMessage": {"go1.7", "go1.0"}, + "syscall.InterfaceMulticastAddrMessage": {"go1.7", "go1.0"}, + "syscall.FormatMessage": {"go1.5", "go1.0"}, + "syscall.PostQueuedCompletionStatus": {"go1.17", "go1.0"}, + "syscall.GetQueuedCompletionStatus": {"go1.17", "go1.0"}, + "syscall.CreateIoCompletionPort": {"go1.17", "go1.0"}, // We choose to only track the package itself, even though all functions are derecated individually, too. Anyone // using ioutil directly will have to import it, and this keeps the noise down. - "io/ioutil": {19, 19}, + "io/ioutil": {"go1.19", "go1.19"}, - "bytes.Title": {18, 0}, - "strings.Title": {18, 0}, - "(crypto/tls.Config).PreferServerCipherSuites": {18, DeprecatedUseNoLonger}, + "bytes.Title": {"go1.18", "go1.0"}, + "strings.Title": {"go1.18", "go1.0"}, + "(crypto/tls.Config).PreferServerCipherSuites": {"go1.18", DeprecatedUseNoLonger}, // It's not clear if Subjects was okay to use in the past, so we err on the less noisy side of assuming that it was. - "(*crypto/x509.CertPool).Subjects": {18, DeprecatedUseNoLonger}, - "go/types.NewSignature": {18, 18}, - "(net.Error).Temporary": {18, DeprecatedNeverUse}, + "(*crypto/x509.CertPool).Subjects": {"go1.18", DeprecatedUseNoLonger}, + "go/types.NewSignature": {"go1.18", "go1.18"}, + "(net.Error).Temporary": {"go1.18", DeprecatedNeverUse}, // InterfaceData is another tricky case. It was deprecated in Go 1.18, but has been useless since Go 1.4, and an // "alternative" (using your own unsafe hacks) has existed forever. We don't want to get into hairsplitting with // users who somehow successfully used this between 1.4 and 1.18, so we'll just tag it as deprecated since 1.18. - "(reflect.Value).InterfaceData": {18, 18}, + "(reflect.Value).InterfaceData": {"go1.18", "go1.18"}, // The following objects are only deprecated on Windows. - "syscall.Syscall": {18, 18}, - "syscall.Syscall12": {18, 18}, - "syscall.Syscall15": {18, 18}, - "syscall.Syscall18": {18, 18}, - "syscall.Syscall6": {18, 18}, - "syscall.Syscall9": {18, 18}, + "syscall.Syscall": {"go1.18", "go1.18"}, + "syscall.Syscall12": {"go1.18", "go1.18"}, + "syscall.Syscall15": {"go1.18", "go1.18"}, + "syscall.Syscall18": {"go1.18", "go1.18"}, + "syscall.Syscall6": {"go1.18", "go1.18"}, + "syscall.Syscall9": {"go1.18", "go1.18"}, - "reflect.SliceHeader": {21, 17}, - "reflect.StringHeader": {21, 20}, - "crypto/elliptic.GenerateKey": {21, 21}, - "crypto/elliptic.Marshal": {21, 21}, - "crypto/elliptic.Unmarshal": {21, 21}, - "(*crypto/elliptic.CurveParams).Add": {21, 21}, - "(*crypto/elliptic.CurveParams).Double": {21, 21}, - "(*crypto/elliptic.CurveParams).IsOnCurve": {21, 21}, - "(*crypto/elliptic.CurveParams).ScalarBaseMult": {21, 21}, - "(*crypto/elliptic.CurveParams).ScalarMult": {21, 21}, - "crypto/rsa.GenerateMultiPrimeKey": {21, DeprecatedNeverUse}, - "(crypto/rsa.PrecomputedValues).CRTValues": {21, DeprecatedNeverUse}, - "(crypto/x509.RevocationList).RevokedCertificates": {21, 21}, + "reflect.SliceHeader": {"go1.21", "go1.17"}, + "reflect.StringHeader": {"go1.21", "go1.20"}, + "crypto/elliptic.GenerateKey": {"go1.21", "go1.21"}, + "crypto/elliptic.Marshal": {"go1.21", "go1.21"}, + "crypto/elliptic.Unmarshal": {"go1.21", "go1.21"}, + "(*crypto/elliptic.CurveParams).Add": {"go1.21", "go1.21"}, + "(*crypto/elliptic.CurveParams).Double": {"go1.21", "go1.21"}, + "(*crypto/elliptic.CurveParams).IsOnCurve": {"go1.21", "go1.21"}, + "(*crypto/elliptic.CurveParams).ScalarBaseMult": {"go1.21", "go1.21"}, + "(*crypto/elliptic.CurveParams).ScalarMult": {"go1.21", "go1.21"}, + "crypto/rsa.GenerateMultiPrimeKey": {"go1.21", DeprecatedNeverUse}, + "(crypto/rsa.PrecomputedValues).CRTValues": {"go1.21", DeprecatedNeverUse}, + "(crypto/x509.RevocationList).RevokedCertificates": {"go1.21", "go1.21"}, + + "go/ast.NewPackage": {"go1.22", "go1.0"}, + "go/ast.Importer": {"go1.22", "go1.0"}, + "go/ast.Object": {"go1.22", "go1.0"}, + "go/ast.Package": {"go1.22", "go1.0"}, + "go/ast.Scope": {"go1.22", "go1.0"}, + "html/template.ErrJSTemplate": {"go1.22", DeprecatedUseNoLonger}, + "reflect.PtrTo": {"go1.22", "go1.18"}, } -// Last imported from Go at c19c4c566c63818dfd059b352e52c4710eecf14d with the following numbers of deprecations: -// -// archive/tar/common.go:2 -// archive/zip/struct.go:6 -// bytes/bytes.go:1 -// compress/flate/inflate.go:2 -// crypto/dsa/dsa.go:1 -// crypto/elliptic/elliptic.go:8 -// crypto/elliptic/params.go:5 -// crypto/rc4/rc4.go:1 -// crypto/rsa/rsa.go:2 -// crypto/tls/common.go:6 -// crypto/x509/cert_pool.go:1 -// crypto/x509/pem_decrypt.go:3 -// crypto/x509/pkix/pkix.go:2 -// crypto/x509/x509.go:6 -// database/sql/driver/driver.go:6 -// debug/gosym/pclntab.go:2 -// encoding/csv/reader.go:2 -// encoding/json/decode.go:1 -// encoding/json/encode.go:1 -// go/build/build.go:1 -// go/doc/comment.go:2 -// go/doc/doc.go:1 -// go/doc/synopsis.go:1 -// go/importer/importer.go:2 -// go/types/interface.go:2 -// go/types/signature.go:1 -// image/geom.go:2 -// image/jpeg/reader.go:1 -// internal/types/errors/codes.go:1 -// io/ioutil/ioutil.go:7 -// io/ioutil/tempfile.go:2 -// math/rand/rand.go:2 -// net/dial.go:2 -// net/http/h2_bundle.go:1 -// net/http/httptest/recorder.go:1 -// net/http/httputil/persist.go:8 -// net/http/request.go:6 -// net/http/server.go:2 -// net/http/socks_bundle.go:1 -// net/http/transport.go:3 -// net/net.go:1 -// os/file.go:1 -// path/filepath/path_plan9.go:1 -// path/filepath/path_unix.go:1 -// path/filepath/path_windows.go:1 -// reflect/value.go:3 -// regexp/regexp.go:1 -// runtime/cpuprof.go:1 -// strings/strings.go:1 -// syscall/bpf_bsd.go:18 -// syscall/bpf_darwin.go:18 -// syscall/dll_windows.go:6 -// syscall/exec_plan9.go:1 -// syscall/exec_unix.go:1 -// syscall/lsf_linux.go:6 -// syscall/route_bsd.go:7 -// syscall/route_darwin.go:1 -// syscall/route_dragonfly.go:2 -// syscall/route_freebsd.go:2 -// syscall/route_netbsd.go:1 -// syscall/route_openbsd.go:1 -// syscall/syscall.go:3 -// syscall/syscall_windows.go:6 -// text/template/parse/node.go:5 -// vendor/golang.org/x/text/transform/transform.go:1 +// Last imported from Go at e8ee1dc4f9e2632ba1018610d1a1187743ae397f diff --git a/tools/vendor/honnef.co/go/tools/knowledge/targets.go b/tools/vendor/honnef.co/go/tools/knowledge/targets.go new file mode 100644 index 0000000000..d9c44f83f2 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/knowledge/targets.go @@ -0,0 +1,38 @@ +package knowledge + +var KnownGOOS = map[string]struct{}{ + "aix": {}, + "android": {}, + "darwin": {}, + "dragonfly": {}, + "freebsd": {}, + "hurd": {}, + "illumos": {}, + "ios": {}, + "js": {}, + "linux": {}, + "netbsd": {}, + "openbsd": {}, + "plan9": {}, + "solaris": {}, + "wasip1": {}, + "windows": {}, +} + +var KnownGOARCH = map[string]struct{}{ + "386": {}, + "amd64": {}, + "arm": {}, + "arm64": {}, + "loong64": {}, + "mips": {}, + "mipsle": {}, + "mips64": {}, + "mips64le": {}, + "ppc64": {}, + "ppc64le": {}, + "riscv64": {}, + "s390x": {}, + "sparc64": {}, + "wasm": {}, +} diff --git a/tools/vendor/honnef.co/go/tools/pattern/match.go b/tools/vendor/honnef.co/go/tools/pattern/match.go index 3eae3bd631..4fb4f8ec64 100644 --- a/tools/vendor/honnef.co/go/tools/pattern/match.go +++ b/tools/vendor/honnef.co/go/tools/pattern/match.go @@ -586,13 +586,28 @@ func (fn Symbol) Match(m *Matcher, node interface{}) (interface{}, bool) { case *types.Builtin: name = obj.Name() case *types.TypeName: - if obj.Pkg() == nil { - return nil, false - } - if obj.Parent() != obj.Pkg().Scope() { - return nil, false + origObj := obj + for { + if obj.Parent() != obj.Pkg().Scope() { + return nil, false + } + name = types.TypeString(obj.Type(), nil) + _, ok = match(m, fn.Name, name) + if ok || !obj.IsAlias() { + return origObj, ok + } else { + // FIXME(dh): we should peel away one layer of alias at a time; this is blocked on + // github.com/golang/go/issues/66559 + switch typ := types.Unalias(obj.Type()).(type) { + case interface{ Obj() *types.TypeName }: + obj = typ.Obj() + case *types.Basic: + return match(m, fn.Name, typ.Name()) + default: + return nil, false + } + } } - name = types.TypeString(obj.Type(), nil) case *types.Const, *types.Var: if obj.Pkg() == nil { return nil, false diff --git a/tools/vendor/honnef.co/go/tools/simple/analysis.go b/tools/vendor/honnef.co/go/tools/simple/analysis.go index 04ac2f5e06..9512d2aa34 100644 --- a/tools/vendor/honnef.co/go/tools/simple/analysis.go +++ b/tools/vendor/honnef.co/go/tools/simple/analysis.go @@ -1,153 +1,80 @@ +// Code generated by generate.go. DO NOT EDIT. + package simple import ( - "golang.org/x/tools/go/analysis" - "golang.org/x/tools/go/analysis/passes/inspect" - "honnef.co/go/tools/analysis/facts/generated" - "honnef.co/go/tools/analysis/facts/purity" "honnef.co/go/tools/analysis/lint" - "honnef.co/go/tools/internal/passes/buildir" + "honnef.co/go/tools/simple/s1000" + "honnef.co/go/tools/simple/s1001" + "honnef.co/go/tools/simple/s1002" + "honnef.co/go/tools/simple/s1003" + "honnef.co/go/tools/simple/s1004" + "honnef.co/go/tools/simple/s1005" + "honnef.co/go/tools/simple/s1006" + "honnef.co/go/tools/simple/s1007" + "honnef.co/go/tools/simple/s1008" + "honnef.co/go/tools/simple/s1009" + "honnef.co/go/tools/simple/s1010" + "honnef.co/go/tools/simple/s1011" + "honnef.co/go/tools/simple/s1012" + "honnef.co/go/tools/simple/s1016" + "honnef.co/go/tools/simple/s1017" + "honnef.co/go/tools/simple/s1018" + "honnef.co/go/tools/simple/s1019" + "honnef.co/go/tools/simple/s1020" + "honnef.co/go/tools/simple/s1021" + "honnef.co/go/tools/simple/s1023" + "honnef.co/go/tools/simple/s1024" + "honnef.co/go/tools/simple/s1025" + "honnef.co/go/tools/simple/s1028" + "honnef.co/go/tools/simple/s1029" + "honnef.co/go/tools/simple/s1030" + "honnef.co/go/tools/simple/s1031" + "honnef.co/go/tools/simple/s1032" + "honnef.co/go/tools/simple/s1033" + "honnef.co/go/tools/simple/s1034" + "honnef.co/go/tools/simple/s1035" + "honnef.co/go/tools/simple/s1036" + "honnef.co/go/tools/simple/s1037" + "honnef.co/go/tools/simple/s1038" + "honnef.co/go/tools/simple/s1039" + "honnef.co/go/tools/simple/s1040" ) -var Analyzers = lint.InitializeAnalyzers(Docs, map[string]*analysis.Analyzer{ - "S1000": { - Run: CheckSingleCaseSelect, - Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, - }, - "S1001": { - Run: CheckLoopCopy, - Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, - }, - "S1002": { - Run: CheckIfBoolCmp, - Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, - }, - "S1003": { - Run: CheckStringsContains, - Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, - }, - "S1004": { - Run: CheckBytesCompare, - Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, - }, - "S1005": { - Run: CheckUnnecessaryBlank, - Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, - }, - "S1006": { - Run: CheckForTrue, - Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, - }, - "S1007": { - Run: CheckRegexpRaw, - Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, - }, - "S1008": { - Run: CheckIfReturn, - Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, - }, - "S1009": { - Run: CheckRedundantNilCheckWithLen, - Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, - }, - "S1010": { - Run: CheckSlicing, - Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, - }, - "S1011": { - Run: CheckLoopAppend, - Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer, purity.Analyzer}, - }, - "S1012": { - Run: CheckTimeSince, - Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, - }, - "S1016": { - Run: CheckSimplerStructConversion, - Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, - }, - "S1017": { - Run: CheckTrim, - Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, - }, - "S1018": { - Run: CheckLoopSlide, - Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, - }, - "S1019": { - Run: CheckMakeLenCap, - Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, - }, - "S1020": { - Run: CheckAssertNotNil, - Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, - }, - "S1021": { - Run: CheckDeclareAssign, - Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, - }, - "S1023": { - Run: CheckRedundantBreak, - Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, - }, - "S1024": { - Run: CheckTimeUntil, - Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, - }, - "S1025": { - Run: CheckRedundantSprintf, - Requires: []*analysis.Analyzer{buildir.Analyzer, inspect.Analyzer, generated.Analyzer}, - }, - "S1028": { - Run: CheckErrorsNewSprintf, - Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, - }, - "S1029": { - Run: CheckRangeStringRunes, - Requires: []*analysis.Analyzer{buildir.Analyzer}, - }, - "S1030": { - Run: CheckBytesBufferConversions, - Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, - }, - "S1031": { - Run: CheckNilCheckAroundRange, - Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, - }, - "S1032": { - Run: CheckSortHelpers, - Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, - }, - "S1033": { - Run: CheckGuardedDelete, - Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, - }, - "S1034": { - Run: CheckSimplifyTypeSwitch, - Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, - }, - "S1035": { - Run: CheckRedundantCanonicalHeaderKey, - Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, - }, - "S1036": { - Run: CheckUnnecessaryGuard, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - "S1037": { - Run: CheckElaborateSleep, - Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, - }, - "S1038": { - Run: CheckPrintSprintf, - Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, - }, - "S1039": { - Run: CheckSprintLiteral, - Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, - }, - "S1040": { - Run: CheckSameTypeTypeAssertion, - Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, - }, -}) +var Analyzers = []*lint.Analyzer{ + s1000.SCAnalyzer, + s1001.SCAnalyzer, + s1002.SCAnalyzer, + s1003.SCAnalyzer, + s1004.SCAnalyzer, + s1005.SCAnalyzer, + s1006.SCAnalyzer, + s1007.SCAnalyzer, + s1008.SCAnalyzer, + s1009.SCAnalyzer, + s1010.SCAnalyzer, + s1011.SCAnalyzer, + s1012.SCAnalyzer, + s1016.SCAnalyzer, + s1017.SCAnalyzer, + s1018.SCAnalyzer, + s1019.SCAnalyzer, + s1020.SCAnalyzer, + s1021.SCAnalyzer, + s1023.SCAnalyzer, + s1024.SCAnalyzer, + s1025.SCAnalyzer, + s1028.SCAnalyzer, + s1029.SCAnalyzer, + s1030.SCAnalyzer, + s1031.SCAnalyzer, + s1032.SCAnalyzer, + s1033.SCAnalyzer, + s1034.SCAnalyzer, + s1035.SCAnalyzer, + s1036.SCAnalyzer, + s1037.SCAnalyzer, + s1038.SCAnalyzer, + s1039.SCAnalyzer, + s1040.SCAnalyzer, +} diff --git a/tools/vendor/honnef.co/go/tools/simple/doc.go b/tools/vendor/honnef.co/go/tools/simple/doc.go index 421f851344..c04bc24c76 100644 --- a/tools/vendor/honnef.co/go/tools/simple/doc.go +++ b/tools/vendor/honnef.co/go/tools/simple/doc.go @@ -1,469 +1,6 @@ +//go:generate go run ../generate.go + // Package simple contains analyzes that simplify code. // All suggestions made by these analyzes are intended to result in objectively simpler code, // and following their advice is recommended. package simple - -import "honnef.co/go/tools/analysis/lint" - -var Docs = lint.Markdownify(map[string]*lint.RawDocumentation{ - "S1000": { - Title: `Use plain channel send or receive instead of single-case select`, - Text: `Select statements with a single case can be replaced with a simple -send or receive.`, - Before: ` -select { -case x := <-ch: - fmt.Println(x) -}`, - After: ` -x := <-ch -fmt.Println(x) -`, - Since: "2017.1", - MergeIf: lint.MergeIfAny, - }, - - "S1001": { - Title: `Replace for loop with call to copy`, - Text: ` -Use \'copy()\' for copying elements from one slice to another. For -arrays of identical size, you can use simple assignment.`, - Before: ` -for i, x := range src { - dst[i] = x -}`, - After: `copy(dst, src)`, - Since: "2017.1", - // MergeIfAll because the types of src and dst might be different under different build tags. - // You shouldn't write code like that… - MergeIf: lint.MergeIfAll, - }, - - "S1002": { - Title: `Omit comparison with boolean constant`, - Before: `if x == true {}`, - After: `if x {}`, - Since: "2017.1", - // MergeIfAll because 'true' might not be the builtin constant under all build tags. - // You shouldn't write code like that… - MergeIf: lint.MergeIfAll, - }, - - "S1003": { - Title: `Replace call to \'strings.Index\' with \'strings.Contains\'`, - Before: `if strings.Index(x, y) != -1 {}`, - After: `if strings.Contains(x, y) {}`, - Since: "2017.1", - MergeIf: lint.MergeIfAny, - }, - - "S1004": { - Title: `Replace call to \'bytes.Compare\' with \'bytes.Equal\'`, - Before: `if bytes.Compare(x, y) == 0 {}`, - After: `if bytes.Equal(x, y) {}`, - Since: "2017.1", - MergeIf: lint.MergeIfAny, - }, - - "S1005": { - Title: `Drop unnecessary use of the blank identifier`, - Text: `In many cases, assigning to the blank identifier is unnecessary.`, - Before: ` -for _ = range s {} -x, _ = someMap[key] -_ = <-ch`, - After: ` -for range s{} -x = someMap[key] -<-ch`, - Since: "2017.1", - MergeIf: lint.MergeIfAny, - }, - - "S1006": { - Title: `Use \"for { ... }\" for infinite loops`, - Text: `For infinite loops, using \'for { ... }\' is the most idiomatic choice.`, - Since: "2017.1", - MergeIf: lint.MergeIfAny, - }, - - "S1007": { - Title: `Simplify regular expression by using raw string literal`, - Text: `Raw string literals use backticks instead of quotation marks and do not support -any escape sequences. This means that the backslash can be used -freely, without the need of escaping. - -Since regular expressions have their own escape sequences, raw strings -can improve their readability.`, - Before: `regexp.Compile("\\A(\\w+) profile: total \\d+\\n\\z")`, - After: "regexp.Compile(`\\A(\\w+) profile: total \\d+\\n\\z`)", - Since: "2017.1", - MergeIf: lint.MergeIfAny, - }, - - "S1008": { - Title: `Simplify returning boolean expression`, - Before: ` -if { - return true -} -return false`, - After: `return `, - Since: "2017.1", - MergeIf: lint.MergeIfAny, - }, - - "S1009": { - Title: `Omit redundant nil check on slices`, - Text: `The \'len\' function is defined for all slices, even nil ones, which have -a length of zero. It is not necessary to check if a slice is not nil -before checking that its length is not zero.`, - Before: `if x != nil && len(x) != 0 {}`, - After: `if len(x) != 0 {}`, - Since: "2017.1", - MergeIf: lint.MergeIfAny, - }, - - "S1010": { - Title: `Omit default slice index`, - Text: `When slicing, the second index defaults to the length of the value, -making \'s[n:len(s)]\' and \'s[n:]\' equivalent.`, - Since: "2017.1", - MergeIf: lint.MergeIfAny, - }, - - "S1011": { - Title: `Use a single \'append\' to concatenate two slices`, - Before: ` -for _, e := range y { - x = append(x, e) -} - -for i := range y { - x = append(x, y[i]) -} - -for i := range y { - v := y[i] - x = append(x, v) -}`, - - After: ` -x = append(x, y...) -x = append(x, y...) -x = append(x, y...)`, - Since: "2017.1", - // MergeIfAll because y might not be a slice under all build tags. - MergeIf: lint.MergeIfAll, - }, - - "S1012": { - Title: `Replace \'time.Now().Sub(x)\' with \'time.Since(x)\'`, - Text: `The \'time.Since\' helper has the same effect as using \'time.Now().Sub(x)\' -but is easier to read.`, - Before: `time.Now().Sub(x)`, - After: `time.Since(x)`, - Since: "2017.1", - MergeIf: lint.MergeIfAny, - }, - - "S1016": { - Title: `Use a type conversion instead of manually copying struct fields`, - Text: `Two struct types with identical fields can be converted between each -other. In older versions of Go, the fields had to have identical -struct tags. Since Go 1.8, however, struct tags are ignored during -conversions. It is thus not necessary to manually copy every field -individually.`, - Before: ` -var x T1 -y := T2{ - Field1: x.Field1, - Field2: x.Field2, -}`, - After: ` -var x T1 -y := T2(x)`, - Since: "2017.1", - MergeIf: lint.MergeIfAll, - }, - - "S1017": { - Title: `Replace manual trimming with \'strings.TrimPrefix\'`, - Text: `Instead of using \'strings.HasPrefix\' and manual slicing, use the -\'strings.TrimPrefix\' function. If the string doesn't start with the -prefix, the original string will be returned. Using \'strings.TrimPrefix\' -reduces complexity, and avoids common bugs, such as off-by-one -mistakes.`, - Before: ` -if strings.HasPrefix(str, prefix) { - str = str[len(prefix):] -}`, - After: `str = strings.TrimPrefix(str, prefix)`, - Since: "2017.1", - MergeIf: lint.MergeIfAny, - }, - - "S1018": { - Title: `Use \"copy\" for sliding elements`, - Text: `\'copy()\' permits using the same source and destination slice, even with -overlapping ranges. This makes it ideal for sliding elements in a -slice.`, - - Before: ` -for i := 0; i < n; i++ { - bs[i] = bs[offset+i] -}`, - After: `copy(bs[:n], bs[offset:])`, - Since: "2017.1", - MergeIf: lint.MergeIfAny, - }, - - "S1019": { - Title: `Simplify \"make\" call by omitting redundant arguments`, - Text: `The \"make\" function has default values for the length and capacity -arguments. For channels, the length defaults to zero, and for slices, -the capacity defaults to the length.`, - Since: "2017.1", - // MergeIfAll because the type might be different under different build tags. - // You shouldn't write code like that… - MergeIf: lint.MergeIfAll, - }, - - "S1020": { - Title: `Omit redundant nil check in type assertion`, - Before: `if _, ok := i.(T); ok && i != nil {}`, - After: `if _, ok := i.(T); ok {}`, - Since: "2017.1", - MergeIf: lint.MergeIfAny, - }, - - "S1021": { - Title: `Merge variable declaration and assignment`, - Before: ` -var x uint -x = 1`, - After: `var x uint = 1`, - Since: "2017.1", - MergeIf: lint.MergeIfAny, - }, - - "S1023": { - Title: `Omit redundant control flow`, - Text: `Functions that have no return value do not need a return statement as -the final statement of the function. - -Switches in Go do not have automatic fallthrough, unlike languages -like C. It is not necessary to have a break statement as the final -statement in a case block.`, - Since: "2017.1", - MergeIf: lint.MergeIfAny, - }, - - "S1024": { - Title: `Replace \'x.Sub(time.Now())\' with \'time.Until(x)\'`, - Text: `The \'time.Until\' helper has the same effect as using \'x.Sub(time.Now())\' -but is easier to read.`, - Before: `x.Sub(time.Now())`, - After: `time.Until(x)`, - Since: "2017.1", - MergeIf: lint.MergeIfAny, - }, - - "S1025": { - Title: `Don't use \'fmt.Sprintf("%s", x)\' unnecessarily`, - Text: `In many instances, there are easier and more efficient ways of getting -a value's string representation. Whenever a value's underlying type is -a string already, or the type has a String method, they should be used -directly. - -Given the following shared definitions - - type T1 string - type T2 int - - func (T2) String() string { return "Hello, world" } - - var x string - var y T1 - var z T2 - -we can simplify - - fmt.Sprintf("%s", x) - fmt.Sprintf("%s", y) - fmt.Sprintf("%s", z) - -to - - x - string(y) - z.String() -`, - Since: "2017.1", - MergeIf: lint.MergeIfAll, - }, - - "S1028": { - Title: `Simplify error construction with \'fmt.Errorf\'`, - Before: `errors.New(fmt.Sprintf(...))`, - After: `fmt.Errorf(...)`, - Since: "2017.1", - MergeIf: lint.MergeIfAny, - }, - - "S1029": { - Title: `Range over the string directly`, - Text: `Ranging over a string will yield byte offsets and runes. If the offset -isn't used, this is functionally equivalent to converting the string -to a slice of runes and ranging over that. Ranging directly over the -string will be more performant, however, as it avoids allocating a new -slice, the size of which depends on the length of the string.`, - Before: `for _, r := range []rune(s) {}`, - After: `for _, r := range s {}`, - Since: "2017.1", - MergeIf: lint.MergeIfAny, - }, - - "S1030": { - Title: `Use \'bytes.Buffer.String\' or \'bytes.Buffer.Bytes\'`, - Text: `\'bytes.Buffer\' has both a \'String\' and a \'Bytes\' method. It is almost never -necessary to use \'string(buf.Bytes())\' or \'[]byte(buf.String())\' – simply -use the other method. - -The only exception to this are map lookups. Due to a compiler optimization, -\'m[string(buf.Bytes())]\' is more efficient than \'m[buf.String()]\'. -`, - Since: "2017.1", - MergeIf: lint.MergeIfAny, - }, - - "S1031": { - Title: `Omit redundant nil check around loop`, - Text: `You can use range on nil slices and maps, the loop will simply never -execute. This makes an additional nil check around the loop -unnecessary.`, - Before: ` -if s != nil { - for _, x := range s { - ... - } -}`, - After: ` -for _, x := range s { - ... -}`, - Since: "2017.1", - // MergeIfAll because x might be a channel under some build tags. - // you shouldn't write code like that… - MergeIf: lint.MergeIfAll, - }, - - "S1032": { - Title: `Use \'sort.Ints(x)\', \'sort.Float64s(x)\', and \'sort.Strings(x)\'`, - Text: `The \'sort.Ints\', \'sort.Float64s\' and \'sort.Strings\' functions are easier to -read than \'sort.Sort(sort.IntSlice(x))\', \'sort.Sort(sort.Float64Slice(x))\' -and \'sort.Sort(sort.StringSlice(x))\'.`, - Before: `sort.Sort(sort.StringSlice(x))`, - After: `sort.Strings(x)`, - Since: "2019.1", - MergeIf: lint.MergeIfAny, - }, - - "S1033": { - Title: `Unnecessary guard around call to \"delete\"`, - Text: `Calling \'delete\' on a nil map is a no-op.`, - Since: "2019.2", - MergeIf: lint.MergeIfAny, - }, - - "S1034": { - Title: `Use result of type assertion to simplify cases`, - Since: "2019.2", - MergeIf: lint.MergeIfAny, - }, - - "S1035": { - Title: `Redundant call to \'net/http.CanonicalHeaderKey\' in method call on \'net/http.Header\'`, - Text: ` -The methods on \'net/http.Header\', namely \'Add\', \'Del\', \'Get\' -and \'Set\', already canonicalize the given header name.`, - Since: "2020.1", - MergeIf: lint.MergeIfAny, - }, - - "S1036": { - Title: `Unnecessary guard around map access`, - - Text: ` -When accessing a map key that doesn't exist yet, one receives a zero -value. Often, the zero value is a suitable value, for example when -using append or doing integer math. - -The following - - if _, ok := m["foo"]; ok { - m["foo"] = append(m["foo"], "bar") - } else { - m["foo"] = []string{"bar"} - } - -can be simplified to - - m["foo"] = append(m["foo"], "bar") - -and - - if _, ok := m2["k"]; ok { - m2["k"] += 4 - } else { - m2["k"] = 4 - } - -can be simplified to - - m["k"] += 4 -`, - Since: "2020.1", - MergeIf: lint.MergeIfAny, - }, - - "S1037": { - Title: `Elaborate way of sleeping`, - Text: `Using a select statement with a single case receiving -from the result of \'time.After\' is a very elaborate way of sleeping that -can much simpler be expressed with a simple call to time.Sleep.`, - Since: "2020.1", - MergeIf: lint.MergeIfAny, - }, - - "S1038": { - Title: "Unnecessarily complex way of printing formatted string", - Text: `Instead of using \'fmt.Print(fmt.Sprintf(...))\', one can use \'fmt.Printf(...)\'.`, - Since: "2020.1", - MergeIf: lint.MergeIfAny, - }, - - "S1039": { - Title: `Unnecessary use of \'fmt.Sprint\'`, - Text: ` -Calling \'fmt.Sprint\' with a single string argument is unnecessary -and identical to using the string directly.`, - Since: "2020.1", - // MergeIfAll because s might not be a string under all build tags. - // you shouldn't write code like that… - MergeIf: lint.MergeIfAll, - }, - "S1040": { - Title: "Type assertion to current type", - Text: `The type assertion \'x.(SomeInterface)\', when \'x\' already has type -\'SomeInterface\', can only fail if \'x\' is nil. Usually, this is -left-over code from when \'x\' had a different type and you can safely -delete the type assertion. If you want to check that \'x\' is not nil, -consider being explicit and using an actual \'if x == nil\' comparison -instead of relying on the type assertion panicking.`, - Since: "2021.1", - // MergeIfAll because x might have different types under different build tags. - // You shouldn't write code like that… - MergeIf: lint.MergeIfAll, - }, -}) diff --git a/tools/vendor/honnef.co/go/tools/simple/lint.go b/tools/vendor/honnef.co/go/tools/simple/lint.go deleted file mode 100644 index 8458f7be69..0000000000 --- a/tools/vendor/honnef.co/go/tools/simple/lint.go +++ /dev/null @@ -1,2192 +0,0 @@ -package simple - -import ( - "fmt" - "go/ast" - "go/constant" - "go/token" - "go/types" - "path/filepath" - "reflect" - "sort" - "strings" - - "honnef.co/go/tools/analysis/code" - "honnef.co/go/tools/analysis/edit" - "honnef.co/go/tools/analysis/facts/purity" - "honnef.co/go/tools/analysis/lint" - "honnef.co/go/tools/analysis/report" - "honnef.co/go/tools/go/ast/astutil" - "honnef.co/go/tools/go/types/typeutil" - "honnef.co/go/tools/internal/passes/buildir" - "honnef.co/go/tools/internal/sharedcheck" - "honnef.co/go/tools/knowledge" - "honnef.co/go/tools/pattern" - - "golang.org/x/exp/typeparams" - "golang.org/x/tools/go/analysis" -) - -var ( - checkSingleCaseSelectQ1 = pattern.MustParse(` - (ForStmt - nil nil nil - select@(SelectStmt - (CommClause - (Or - (UnaryExpr "<-" _) - (AssignStmt _ _ (UnaryExpr "<-" _))) - _)))`) - checkSingleCaseSelectQ2 = pattern.MustParse(`(SelectStmt (CommClause _ _))`) -) - -func CheckSingleCaseSelect(pass *analysis.Pass) (interface{}, error) { - seen := map[ast.Node]struct{}{} - fn := func(node ast.Node) { - if m, ok := code.Match(pass, checkSingleCaseSelectQ1, node); ok { - seen[m.State["select"].(ast.Node)] = struct{}{} - report.Report(pass, node, "should use for range instead of for { select {} }", report.FilterGenerated()) - } else if _, ok := code.Match(pass, checkSingleCaseSelectQ2, node); ok { - if _, ok := seen[node]; !ok { - report.Report(pass, node, "should use a simple channel send/receive instead of select with a single case", - report.ShortRange(), - report.FilterGenerated()) - } - } - } - code.Preorder(pass, fn, (*ast.ForStmt)(nil), (*ast.SelectStmt)(nil)) - return nil, nil -} - -var ( - checkLoopCopyQ = pattern.MustParse(` - (Or - (RangeStmt - key@(Ident _) value@(Ident _) ":=" src - [(AssignStmt (IndexExpr dst key) "=" value)]) - (RangeStmt - key@(Ident _) nil ":=" src - [(AssignStmt (IndexExpr dst key) "=" (IndexExpr src key))]) - (ForStmt - (AssignStmt key@(Ident _) ":=" (IntegerLiteral "0")) - (BinaryExpr key "<" (CallExpr (Symbol "len") [src])) - (IncDecStmt key "++") - [(AssignStmt (IndexExpr dst key) "=" (IndexExpr src key))]))`) -) - -func CheckLoopCopy(pass *analysis.Pass) (interface{}, error) { - // TODO revisit once range doesn't require a structural type - - isInvariant := func(k, v types.Object, node ast.Expr) bool { - if code.MayHaveSideEffects(pass, node, nil) { - return false - } - invariant := true - ast.Inspect(node, func(node ast.Node) bool { - if node, ok := node.(*ast.Ident); ok { - obj := pass.TypesInfo.ObjectOf(node) - if obj == k || obj == v { - // don't allow loop bodies like 'a[i][i] = v' - invariant = false - return false - } - } - return true - }) - return invariant - } - - var elType func(T types.Type) (el types.Type, isArray bool, isArrayPointer bool, ok bool) - elType = func(T types.Type) (el types.Type, isArray bool, isArrayPointer bool, ok bool) { - switch typ := T.Underlying().(type) { - case *types.Slice: - return typ.Elem(), false, false, true - case *types.Array: - return typ.Elem(), true, false, true - case *types.Pointer: - el, isArray, _, ok = elType(typ.Elem()) - return el, isArray, true, ok - default: - return nil, false, false, false - } - } - - fn := func(node ast.Node) { - m, ok := code.Match(pass, checkLoopCopyQ, node) - if !ok { - return - } - - src := m.State["src"].(ast.Expr) - dst := m.State["dst"].(ast.Expr) - - k := pass.TypesInfo.ObjectOf(m.State["key"].(*ast.Ident)) - var v types.Object - if value, ok := m.State["value"]; ok { - v = pass.TypesInfo.ObjectOf(value.(*ast.Ident)) - } - if !isInvariant(k, v, dst) { - return - } - if !isInvariant(k, v, src) { - // For example: 'for i := range foo()' - return - } - - Tsrc := pass.TypesInfo.TypeOf(src) - Tdst := pass.TypesInfo.TypeOf(dst) - TsrcElem, TsrcArray, TsrcPointer, ok := elType(Tsrc) - if !ok { - return - } - if TsrcPointer { - Tsrc = Tsrc.Underlying().(*types.Pointer).Elem() - } - TdstElem, TdstArray, TdstPointer, ok := elType(Tdst) - if !ok { - return - } - if TdstPointer { - Tdst = Tdst.Underlying().(*types.Pointer).Elem() - } - - if !types.Identical(TsrcElem, TdstElem) { - return - } - - if TsrcArray && TdstArray && types.Identical(Tsrc, Tdst) { - if TsrcPointer { - src = &ast.StarExpr{ - X: src, - } - } - if TdstPointer { - dst = &ast.StarExpr{ - X: dst, - } - } - r := &ast.AssignStmt{ - Lhs: []ast.Expr{dst}, - Rhs: []ast.Expr{src}, - Tok: token.ASSIGN, - } - - report.Report(pass, node, "should copy arrays using assignment instead of using a loop", - report.FilterGenerated(), - report.ShortRange(), - report.Fixes(edit.Fix("replace loop with assignment", edit.ReplaceWithNode(pass.Fset, node, r)))) - } else { - tv, err := types.Eval(pass.Fset, pass.Pkg, node.Pos(), "copy") - if err == nil && tv.IsBuiltin() { - to := "to" - from := "from" - src := m.State["src"].(ast.Expr) - if TsrcArray { - from = "from[:]" - src = &ast.SliceExpr{ - X: src, - } - } - dst := m.State["dst"].(ast.Expr) - if TdstArray { - to = "to[:]" - dst = &ast.SliceExpr{ - X: dst, - } - } - - r := &ast.CallExpr{ - Fun: &ast.Ident{Name: "copy"}, - Args: []ast.Expr{dst, src}, - } - opts := []report.Option{ - report.ShortRange(), - report.FilterGenerated(), - report.Fixes(edit.Fix("replace loop with call to copy()", edit.ReplaceWithNode(pass.Fset, node, r))), - } - report.Report(pass, node, fmt.Sprintf("should use copy(%s, %s) instead of a loop", to, from), opts...) - } - } - } - code.Preorder(pass, fn, (*ast.ForStmt)(nil), (*ast.RangeStmt)(nil)) - return nil, nil -} - -func CheckIfBoolCmp(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - if code.IsInTest(pass, node) { - return - } - - expr := node.(*ast.BinaryExpr) - if expr.Op != token.EQL && expr.Op != token.NEQ { - return - } - x := code.IsBoolConst(pass, expr.X) - y := code.IsBoolConst(pass, expr.Y) - if !x && !y { - return - } - var other ast.Expr - var val bool - if x { - val = code.BoolConst(pass, expr.X) - other = expr.Y - } else { - val = code.BoolConst(pass, expr.Y) - other = expr.X - } - - ok := typeutil.All(pass.TypesInfo.TypeOf(other), func(term *types.Term) bool { - basic, ok := term.Type().Underlying().(*types.Basic) - return ok && basic.Kind() == types.Bool - }) - if !ok { - return - } - op := "" - if (expr.Op == token.EQL && !val) || (expr.Op == token.NEQ && val) { - op = "!" - } - r := op + report.Render(pass, other) - l1 := len(r) - r = strings.TrimLeft(r, "!") - if (l1-len(r))%2 == 1 { - r = "!" + r - } - report.Report(pass, expr, fmt.Sprintf("should omit comparison to bool constant, can be simplified to %s", r), - report.FilterGenerated(), - report.Fixes(edit.Fix("simplify bool comparison", edit.ReplaceWithString(expr, r)))) - } - code.Preorder(pass, fn, (*ast.BinaryExpr)(nil)) - return nil, nil -} - -var ( - checkBytesBufferConversionsQ = pattern.MustParse(`(CallExpr _ [(CallExpr sel@(SelectorExpr recv _) [])])`) - checkBytesBufferConversionsRs = pattern.MustParse(`(CallExpr (SelectorExpr recv (Ident "String")) [])`) - checkBytesBufferConversionsRb = pattern.MustParse(`(CallExpr (SelectorExpr recv (Ident "Bytes")) [])`) -) - -func CheckBytesBufferConversions(pass *analysis.Pass) (interface{}, error) { - if pass.Pkg.Path() == "bytes" || pass.Pkg.Path() == "bytes_test" { - // The bytes package can use itself however it wants - return nil, nil - } - fn := func(node ast.Node, stack []ast.Node) { - m, ok := code.Match(pass, checkBytesBufferConversionsQ, node) - if !ok { - return - } - call := node.(*ast.CallExpr) - sel := m.State["sel"].(*ast.SelectorExpr) - - typ := pass.TypesInfo.TypeOf(call.Fun) - if typ == types.Universe.Lookup("string").Type() && code.IsCallTo(pass, call.Args[0], "(*bytes.Buffer).Bytes") { - if _, ok := stack[len(stack)-2].(*ast.IndexExpr); ok { - // Don't flag m[string(buf.Bytes())] – thanks to a - // compiler optimization, this is actually faster than - // m[buf.String()] - return - } - - report.Report(pass, call, fmt.Sprintf("should use %v.String() instead of %v", report.Render(pass, sel.X), report.Render(pass, call)), - report.FilterGenerated(), - report.Fixes(edit.Fix("simplify conversion", edit.ReplaceWithPattern(pass.Fset, node, checkBytesBufferConversionsRs, m.State)))) - } else if typ, ok := typ.(*types.Slice); ok && typ.Elem() == types.Universe.Lookup("byte").Type() && code.IsCallTo(pass, call.Args[0], "(*bytes.Buffer).String") { - report.Report(pass, call, fmt.Sprintf("should use %v.Bytes() instead of %v", report.Render(pass, sel.X), report.Render(pass, call)), - report.FilterGenerated(), - report.Fixes(edit.Fix("simplify conversion", edit.ReplaceWithPattern(pass.Fset, node, checkBytesBufferConversionsRb, m.State)))) - } - - } - code.PreorderStack(pass, fn, (*ast.CallExpr)(nil)) - return nil, nil -} - -func CheckStringsContains(pass *analysis.Pass) (interface{}, error) { - // map of value to token to bool value - allowed := map[int64]map[token.Token]bool{ - -1: {token.GTR: true, token.NEQ: true, token.EQL: false}, - 0: {token.GEQ: true, token.LSS: false}, - } - fn := func(node ast.Node) { - expr := node.(*ast.BinaryExpr) - switch expr.Op { - case token.GEQ, token.GTR, token.NEQ, token.LSS, token.EQL: - default: - return - } - - value, ok := code.ExprToInt(pass, expr.Y) - if !ok { - return - } - - allowedOps, ok := allowed[value] - if !ok { - return - } - b, ok := allowedOps[expr.Op] - if !ok { - return - } - - call, ok := expr.X.(*ast.CallExpr) - if !ok { - return - } - sel, ok := call.Fun.(*ast.SelectorExpr) - if !ok { - return - } - pkgIdent, ok := sel.X.(*ast.Ident) - if !ok { - return - } - funIdent := sel.Sel - if pkgIdent.Name != "strings" && pkgIdent.Name != "bytes" { - return - } - - var r ast.Expr - switch funIdent.Name { - case "IndexRune": - r = &ast.SelectorExpr{ - X: pkgIdent, - Sel: &ast.Ident{Name: "ContainsRune"}, - } - case "IndexAny": - r = &ast.SelectorExpr{ - X: pkgIdent, - Sel: &ast.Ident{Name: "ContainsAny"}, - } - case "Index": - r = &ast.SelectorExpr{ - X: pkgIdent, - Sel: &ast.Ident{Name: "Contains"}, - } - default: - return - } - - r = &ast.CallExpr{ - Fun: r, - Args: call.Args, - } - if !b { - r = &ast.UnaryExpr{ - Op: token.NOT, - X: r, - } - } - - report.Report(pass, node, fmt.Sprintf("should use %s instead", report.Render(pass, r)), - report.FilterGenerated(), - report.Fixes(edit.Fix(fmt.Sprintf("simplify use of %s", report.Render(pass, call.Fun)), edit.ReplaceWithNode(pass.Fset, node, r)))) - } - code.Preorder(pass, fn, (*ast.BinaryExpr)(nil)) - return nil, nil -} - -var ( - checkBytesCompareQ = pattern.MustParse(`(BinaryExpr (CallExpr (Symbol "bytes.Compare") args) op@(Or "==" "!=") (IntegerLiteral "0"))`) - checkBytesCompareRe = pattern.MustParse(`(CallExpr (SelectorExpr (Ident "bytes") (Ident "Equal")) args)`) - checkBytesCompareRn = pattern.MustParse(`(UnaryExpr "!" (CallExpr (SelectorExpr (Ident "bytes") (Ident "Equal")) args))`) -) - -func CheckBytesCompare(pass *analysis.Pass) (interface{}, error) { - if pass.Pkg.Path() == "bytes" || pass.Pkg.Path() == "bytes_test" { - // the bytes package is free to use bytes.Compare as it sees fit - return nil, nil - } - fn := func(node ast.Node) { - m, ok := code.Match(pass, checkBytesCompareQ, node) - if !ok { - return - } - - args := report.RenderArgs(pass, m.State["args"].([]ast.Expr)) - prefix := "" - if m.State["op"].(token.Token) == token.NEQ { - prefix = "!" - } - - var fix analysis.SuggestedFix - switch tok := m.State["op"].(token.Token); tok { - case token.EQL: - fix = edit.Fix("simplify use of bytes.Compare", edit.ReplaceWithPattern(pass.Fset, node, checkBytesCompareRe, m.State)) - case token.NEQ: - fix = edit.Fix("simplify use of bytes.Compare", edit.ReplaceWithPattern(pass.Fset, node, checkBytesCompareRn, m.State)) - default: - panic(fmt.Sprintf("unexpected token %v", tok)) - } - report.Report(pass, node, fmt.Sprintf("should use %sbytes.Equal(%s) instead", prefix, args), report.FilterGenerated(), report.Fixes(fix)) - } - code.Preorder(pass, fn, (*ast.BinaryExpr)(nil)) - return nil, nil -} - -func CheckForTrue(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - loop := node.(*ast.ForStmt) - if loop.Init != nil || loop.Post != nil { - return - } - if !code.IsBoolConst(pass, loop.Cond) || !code.BoolConst(pass, loop.Cond) { - return - } - report.Report(pass, loop, "should use for {} instead of for true {}", - report.ShortRange(), - report.FilterGenerated()) - } - code.Preorder(pass, fn, (*ast.ForStmt)(nil)) - return nil, nil -} - -func CheckRegexpRaw(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - call := node.(*ast.CallExpr) - if !code.IsCallToAny(pass, call, "regexp.MustCompile", "regexp.Compile") { - return - } - sel, ok := call.Fun.(*ast.SelectorExpr) - if !ok { - return - } - lit, ok := call.Args[knowledge.Arg("regexp.Compile.expr")].(*ast.BasicLit) - if !ok { - // TODO(dominikh): support string concat, maybe support constants - return - } - if lit.Kind != token.STRING { - // invalid function call - return - } - if lit.Value[0] != '"' { - // already a raw string - return - } - val := lit.Value - if !strings.Contains(val, `\\`) { - return - } - if strings.Contains(val, "`") { - return - } - - bs := false - for _, c := range val { - if !bs && c == '\\' { - bs = true - continue - } - if bs && c == '\\' { - bs = false - continue - } - if bs { - // backslash followed by non-backslash -> escape sequence - return - } - } - - report.Report(pass, call, fmt.Sprintf("should use raw string (`...`) with regexp.%s to avoid having to escape twice", sel.Sel.Name), report.FilterGenerated()) - } - code.Preorder(pass, fn, (*ast.CallExpr)(nil)) - return nil, nil -} - -var ( - checkIfReturnQIf = pattern.MustParse(`(IfStmt nil cond [(ReturnStmt [ret@(Builtin (Or "true" "false"))])] nil)`) - checkIfReturnQRet = pattern.MustParse(`(ReturnStmt [ret@(Builtin (Or "true" "false"))])`) -) - -func CheckIfReturn(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - block := node.(*ast.BlockStmt) - l := len(block.List) - if l < 2 { - return - } - n1, n2 := block.List[l-2], block.List[l-1] - - if len(block.List) >= 3 { - if _, ok := block.List[l-3].(*ast.IfStmt); ok { - // Do not flag a series of if statements - return - } - } - m1, ok := code.Match(pass, checkIfReturnQIf, n1) - if !ok { - return - } - m2, ok := code.Match(pass, checkIfReturnQRet, n2) - if !ok { - return - } - - if op, ok := m1.State["cond"].(*ast.BinaryExpr); ok { - switch op.Op { - case token.EQL, token.LSS, token.GTR, token.NEQ, token.LEQ, token.GEQ: - default: - return - } - } - - ret1 := m1.State["ret"].(*ast.Ident) - ret2 := m2.State["ret"].(*ast.Ident) - - if ret1.Name == ret2.Name { - // we want the function to return true and false, not the - // same value both times. - return - } - - cond := m1.State["cond"].(ast.Expr) - origCond := cond - if ret1.Name == "false" { - cond = negate(cond) - } - report.Report(pass, n1, - fmt.Sprintf("should use 'return %s' instead of 'if %s { return %s }; return %s'", - report.Render(pass, cond), - report.Render(pass, origCond), report.Render(pass, ret1), report.Render(pass, ret2)), - report.FilterGenerated()) - } - code.Preorder(pass, fn, (*ast.BlockStmt)(nil)) - return nil, nil -} - -func negate(expr ast.Expr) ast.Expr { - switch expr := expr.(type) { - case *ast.BinaryExpr: - out := *expr - switch expr.Op { - case token.EQL: - out.Op = token.NEQ - case token.LSS: - out.Op = token.GEQ - case token.GTR: - out.Op = token.LEQ - case token.NEQ: - out.Op = token.EQL - case token.LEQ: - out.Op = token.GTR - case token.GEQ: - out.Op = token.LSS - } - return &out - case *ast.Ident, *ast.CallExpr, *ast.IndexExpr, *ast.StarExpr: - return &ast.UnaryExpr{ - Op: token.NOT, - X: expr, - } - case *ast.UnaryExpr: - if expr.Op == token.NOT { - return expr.X - } - return &ast.UnaryExpr{ - Op: token.NOT, - X: expr, - } - default: - return &ast.UnaryExpr{ - Op: token.NOT, - X: &ast.ParenExpr{ - X: expr, - }, - } - } -} - -// CheckRedundantNilCheckWithLen checks for the following redundant nil-checks: -// -// if x == nil || len(x) == 0 {} -// if x != nil && len(x) != 0 {} -// if x != nil && len(x) == N {} (where N != 0) -// if x != nil && len(x) > N {} -// if x != nil && len(x) >= N {} (where N != 0) -func CheckRedundantNilCheckWithLen(pass *analysis.Pass) (interface{}, error) { - isConstZero := func(expr ast.Expr) (isConst bool, isZero bool) { - _, ok := expr.(*ast.BasicLit) - if ok { - return true, code.IsIntegerLiteral(pass, expr, constant.MakeInt64(0)) - } - id, ok := expr.(*ast.Ident) - if !ok { - return false, false - } - c, ok := pass.TypesInfo.ObjectOf(id).(*types.Const) - if !ok { - return false, false - } - return true, c.Val().Kind() == constant.Int && c.Val().String() == "0" - } - - fn := func(node ast.Node) { - // check that expr is "x || y" or "x && y" - expr := node.(*ast.BinaryExpr) - if expr.Op != token.LOR && expr.Op != token.LAND { - return - } - eqNil := expr.Op == token.LOR - - // check that x is "xx == nil" or "xx != nil" - x, ok := expr.X.(*ast.BinaryExpr) - if !ok { - return - } - if eqNil && x.Op != token.EQL { - return - } - if !eqNil && x.Op != token.NEQ { - return - } - xx, ok := x.X.(*ast.Ident) - if !ok { - return - } - if !code.IsNil(pass, x.Y) { - return - } - - // check that y is "len(xx) == 0" or "len(xx) ... " - y, ok := expr.Y.(*ast.BinaryExpr) - if !ok { - return - } - if eqNil && y.Op != token.EQL { // must be len(xx) *==* 0 - return - } - yx, ok := y.X.(*ast.CallExpr) - if !ok { - return - } - if !code.IsCallTo(pass, yx, "len") { - return - } - yxArg, ok := yx.Args[knowledge.Arg("len.v")].(*ast.Ident) - if !ok { - return - } - if yxArg.Name != xx.Name { - return - } - - if eqNil && !code.IsIntegerLiteral(pass, y.Y, constant.MakeInt64(0)) { // must be len(x) == *0* - return - } - - if !eqNil { - isConst, isZero := isConstZero(y.Y) - if !isConst { - return - } - switch y.Op { - case token.EQL: - // avoid false positive for "xx != nil && len(xx) == 0" - if isZero { - return - } - case token.GEQ: - // avoid false positive for "xx != nil && len(xx) >= 0" - if isZero { - return - } - case token.NEQ: - // avoid false positive for "xx != nil && len(xx) != " - if !isZero { - return - } - case token.GTR: - // ok - default: - return - } - } - - // finally check that xx type is one of array, slice, map or chan - // this is to prevent false positive in case if xx is a pointer to an array - typ := pass.TypesInfo.TypeOf(xx) - ok = typeutil.All(typ, func(term *types.Term) bool { - switch term.Type().Underlying().(type) { - case *types.Slice: - return true - case *types.Map: - return true - case *types.Chan: - return true - case *types.Pointer: - return false - case *types.TypeParam: - return false - default: - lint.ExhaustiveTypeSwitch(term.Type().Underlying()) - return false - } - }) - if !ok { - return - } - - report.Report(pass, expr, fmt.Sprintf("should omit nil check; len() for %s is defined as zero", typ), report.FilterGenerated()) - } - code.Preorder(pass, fn, (*ast.BinaryExpr)(nil)) - return nil, nil -} - -var checkSlicingQ = pattern.MustParse(`(SliceExpr x@(Object _) low (CallExpr (Builtin "len") [x]) nil)`) - -func CheckSlicing(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - if _, ok := code.Match(pass, checkSlicingQ, node); ok { - expr := node.(*ast.SliceExpr) - report.Report(pass, expr.High, - "should omit second index in slice, s[a:len(s)] is identical to s[a:]", - report.FilterGenerated(), - report.Fixes(edit.Fix("simplify slice expression", edit.Delete(expr.High)))) - } - } - code.Preorder(pass, fn, (*ast.SliceExpr)(nil)) - return nil, nil -} - -func refersTo(pass *analysis.Pass, expr ast.Expr, ident types.Object) bool { - found := false - fn := func(node ast.Node) bool { - ident2, ok := node.(*ast.Ident) - if !ok { - return true - } - if ident == pass.TypesInfo.ObjectOf(ident2) { - found = true - return false - } - return true - } - ast.Inspect(expr, fn) - return found -} - -var checkLoopAppendQ = pattern.MustParse(` -(Or - (RangeStmt - (Ident "_") - val@(Object _) - _ - x - [(AssignStmt [lhs] "=" [(CallExpr (Builtin "append") [lhs val])])]) - (RangeStmt - idx@(Object _) - nil - _ - x - [(AssignStmt [lhs] "=" [(CallExpr (Builtin "append") [lhs (IndexExpr x idx)])])]) - (RangeStmt - idx@(Object _) - nil - _ - x - [(AssignStmt val@(Object _) ":=" (IndexExpr x idx)) - (AssignStmt [lhs] "=" [(CallExpr (Builtin "append") [lhs val])])]))`) - -func CheckLoopAppend(pass *analysis.Pass) (interface{}, error) { - pure := pass.ResultOf[purity.Analyzer].(purity.Result) - - fn := func(node ast.Node) { - m, ok := code.Match(pass, checkLoopAppendQ, node) - if !ok { - return - } - - if val, ok := m.State["val"].(types.Object); ok && refersTo(pass, m.State["lhs"].(ast.Expr), val) { - return - } - - if m.State["idx"] != nil && code.MayHaveSideEffects(pass, m.State["x"].(ast.Expr), pure) { - // When using an index-based loop, x gets evaluated repeatedly and thus should be pure. - // This doesn't matter for value-based loops, because x only gets evaluated once. - return - } - - if idx, ok := m.State["idx"].(types.Object); ok && refersTo(pass, m.State["lhs"].(ast.Expr), idx) { - // The lhs mustn't refer to the index loop variable. - return - } - - if code.MayHaveSideEffects(pass, m.State["lhs"].(ast.Expr), pure) { - // The lhs may be dynamic and return different values on each iteration. For example: - // - // func bar() map[int][]int { /* return one of several maps */ } - // - // func foo(x []int, y [][]int) { - // for i := range x { - // bar()[0] = append(bar()[0], x[i]) - // } - // } - // - // The dynamic nature of the lhs might also affect the value of the index. - return - } - - src := pass.TypesInfo.TypeOf(m.State["x"].(ast.Expr)) - dst := pass.TypesInfo.TypeOf(m.State["lhs"].(ast.Expr)) - if !types.Identical(src, dst) { - return - } - - r := &ast.AssignStmt{ - Lhs: []ast.Expr{m.State["lhs"].(ast.Expr)}, - Tok: token.ASSIGN, - Rhs: []ast.Expr{ - &ast.CallExpr{ - Fun: &ast.Ident{Name: "append"}, - Args: []ast.Expr{ - m.State["lhs"].(ast.Expr), - m.State["x"].(ast.Expr), - }, - Ellipsis: 1, - }, - }, - } - - report.Report(pass, node, fmt.Sprintf("should replace loop with %s", report.Render(pass, r)), - report.ShortRange(), - report.FilterGenerated(), - report.Fixes(edit.Fix("replace loop with call to append", edit.ReplaceWithNode(pass.Fset, node, r)))) - } - code.Preorder(pass, fn, (*ast.RangeStmt)(nil)) - return nil, nil -} - -var ( - checkTimeSinceQ = pattern.MustParse(`(CallExpr (SelectorExpr (CallExpr (Symbol "time.Now") []) (Symbol "(time.Time).Sub")) [arg])`) - checkTimeSinceR = pattern.MustParse(`(CallExpr (SelectorExpr (Ident "time") (Ident "Since")) [arg])`) -) - -func CheckTimeSince(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - if _, edits, ok := code.MatchAndEdit(pass, checkTimeSinceQ, checkTimeSinceR, node); ok { - report.Report(pass, node, "should use time.Since instead of time.Now().Sub", - report.FilterGenerated(), - report.Fixes(edit.Fix("replace with call to time.Since", edits...))) - } - } - code.Preorder(pass, fn, (*ast.CallExpr)(nil)) - return nil, nil -} - -var ( - checkTimeUntilQ = pattern.MustParse(`(CallExpr (Symbol "(time.Time).Sub") [(CallExpr (Symbol "time.Now") [])])`) - checkTimeUntilR = pattern.MustParse(`(CallExpr (SelectorExpr (Ident "time") (Ident "Until")) [arg])`) -) - -func CheckTimeUntil(pass *analysis.Pass) (interface{}, error) { - if !code.IsGoVersion(pass, 8) { - return nil, nil - } - fn := func(node ast.Node) { - if _, ok := code.Match(pass, checkTimeUntilQ, node); ok { - if sel, ok := node.(*ast.CallExpr).Fun.(*ast.SelectorExpr); ok { - r := pattern.NodeToAST(checkTimeUntilR.Root, map[string]interface{}{"arg": sel.X}).(ast.Node) - report.Report(pass, node, "should use time.Until instead of t.Sub(time.Now())", - report.FilterGenerated(), - report.Fixes(edit.Fix("replace with call to time.Until", edit.ReplaceWithNode(pass.Fset, node, r)))) - } else { - report.Report(pass, node, "should use time.Until instead of t.Sub(time.Now())", report.FilterGenerated()) - } - } - } - code.Preorder(pass, fn, (*ast.CallExpr)(nil)) - return nil, nil -} - -var ( - checkUnnecessaryBlankQ1 = pattern.MustParse(` - (AssignStmt - [_ (Ident "_")] - _ - (Or - (IndexExpr _ _) - (UnaryExpr "<-" _))) `) - checkUnnecessaryBlankQ2 = pattern.MustParse(` - (AssignStmt - (Ident "_") _ recv@(UnaryExpr "<-" _))`) -) - -func CheckUnnecessaryBlank(pass *analysis.Pass) (interface{}, error) { - fn1 := func(node ast.Node) { - if _, ok := code.Match(pass, checkUnnecessaryBlankQ1, node); ok { - r := *node.(*ast.AssignStmt) - r.Lhs = r.Lhs[0:1] - report.Report(pass, node, "unnecessary assignment to the blank identifier", - report.FilterGenerated(), - report.Fixes(edit.Fix("remove assignment to blank identifier", edit.ReplaceWithNode(pass.Fset, node, &r)))) - } else if m, ok := code.Match(pass, checkUnnecessaryBlankQ2, node); ok { - report.Report(pass, node, "unnecessary assignment to the blank identifier", - report.FilterGenerated(), - report.Fixes(edit.Fix("simplify channel receive operation", edit.ReplaceWithNode(pass.Fset, node, m.State["recv"].(ast.Node))))) - } - } - - fn3 := func(node ast.Node) { - rs := node.(*ast.RangeStmt) - - // for _ - if rs.Value == nil && astutil.IsBlank(rs.Key) { - report.Report(pass, rs.Key, "unnecessary assignment to the blank identifier", - report.FilterGenerated(), - report.Fixes(edit.Fix("remove assignment to blank identifier", edit.Delete(edit.Range{rs.Key.Pos(), rs.TokPos + 1})))) - } - - // for _, _ - if astutil.IsBlank(rs.Key) && astutil.IsBlank(rs.Value) { - // FIXME we should mark both key and value - report.Report(pass, rs.Key, "unnecessary assignment to the blank identifier", - report.FilterGenerated(), - report.Fixes(edit.Fix("remove assignment to blank identifier", edit.Delete(edit.Range{rs.Key.Pos(), rs.TokPos + 1})))) - } - - // for x, _ - if !astutil.IsBlank(rs.Key) && astutil.IsBlank(rs.Value) { - report.Report(pass, rs.Value, "unnecessary assignment to the blank identifier", - report.FilterGenerated(), - report.Fixes(edit.Fix("remove assignment to blank identifier", edit.Delete(edit.Range{rs.Key.End(), rs.Value.End()})))) - } - } - - code.Preorder(pass, fn1, (*ast.AssignStmt)(nil)) - if code.IsGoVersion(pass, 4) { - code.Preorder(pass, fn3, (*ast.RangeStmt)(nil)) - } - return nil, nil -} - -func CheckSimplerStructConversion(pass *analysis.Pass) (interface{}, error) { - // TODO(dh): support conversions between type parameters - fn := func(node ast.Node, stack []ast.Node) { - if unary, ok := stack[len(stack)-2].(*ast.UnaryExpr); ok && unary.Op == token.AND { - // Do not suggest type conversion between pointers - return - } - - lit := node.(*ast.CompositeLit) - typ1, _ := pass.TypesInfo.TypeOf(lit.Type).(*types.Named) - if typ1 == nil { - return - } - s1, ok := typ1.Underlying().(*types.Struct) - if !ok { - return - } - - var typ2 *types.Named - var ident *ast.Ident - getSelType := func(expr ast.Expr) (types.Type, *ast.Ident, bool) { - sel, ok := expr.(*ast.SelectorExpr) - if !ok { - return nil, nil, false - } - ident, ok := sel.X.(*ast.Ident) - if !ok { - return nil, nil, false - } - typ := pass.TypesInfo.TypeOf(sel.X) - return typ, ident, typ != nil - } - if len(lit.Elts) == 0 { - return - } - if s1.NumFields() != len(lit.Elts) { - return - } - for i, elt := range lit.Elts { - var t types.Type - var id *ast.Ident - var ok bool - switch elt := elt.(type) { - case *ast.SelectorExpr: - t, id, ok = getSelType(elt) - if !ok { - return - } - if i >= s1.NumFields() || s1.Field(i).Name() != elt.Sel.Name { - return - } - case *ast.KeyValueExpr: - var sel *ast.SelectorExpr - sel, ok = elt.Value.(*ast.SelectorExpr) - if !ok { - return - } - - if elt.Key.(*ast.Ident).Name != sel.Sel.Name { - return - } - t, id, ok = getSelType(elt.Value) - } - if !ok { - return - } - // All fields must be initialized from the same object - if ident != nil && pass.TypesInfo.ObjectOf(ident) != pass.TypesInfo.ObjectOf(id) { - return - } - typ2, _ = t.(*types.Named) - if typ2 == nil { - return - } - ident = id - } - - if typ2 == nil { - return - } - - if typ1.Obj().Pkg() != typ2.Obj().Pkg() { - // Do not suggest type conversions between different - // packages. Types in different packages might only match - // by coincidence. Furthermore, if the dependency ever - // adds more fields to its type, it could break the code - // that relies on the type conversion to work. - return - } - - s2, ok := typ2.Underlying().(*types.Struct) - if !ok { - return - } - if typ1 == typ2 { - return - } - if code.IsGoVersion(pass, 8) { - if !types.IdenticalIgnoreTags(s1, s2) { - return - } - } else { - if !types.Identical(s1, s2) { - return - } - } - - r := &ast.CallExpr{ - Fun: lit.Type, - Args: []ast.Expr{ident}, - } - report.Report(pass, node, - fmt.Sprintf("should convert %s (type %s) to %s instead of using struct literal", ident.Name, types.TypeString(typ2, types.RelativeTo(pass.Pkg)), types.TypeString(typ1, types.RelativeTo(pass.Pkg))), - report.FilterGenerated(), - report.Fixes(edit.Fix("use type conversion", edit.ReplaceWithNode(pass.Fset, node, r)))) - } - code.PreorderStack(pass, fn, (*ast.CompositeLit)(nil)) - return nil, nil -} - -func CheckTrim(pass *analysis.Pass) (interface{}, error) { - sameNonDynamic := func(node1, node2 ast.Node) bool { - if reflect.TypeOf(node1) != reflect.TypeOf(node2) { - return false - } - - switch node1 := node1.(type) { - case *ast.Ident: - return pass.TypesInfo.ObjectOf(node1) == pass.TypesInfo.ObjectOf(node2.(*ast.Ident)) - case *ast.SelectorExpr, *ast.IndexExpr: - return astutil.Equal(node1, node2) - case *ast.BasicLit: - return astutil.Equal(node1, node2) - } - return false - } - - isLenOnIdent := func(fn ast.Expr, ident ast.Expr) bool { - call, ok := fn.(*ast.CallExpr) - if !ok { - return false - } - if !code.IsCallTo(pass, call, "len") { - return false - } - if len(call.Args) != 1 { - return false - } - return sameNonDynamic(call.Args[knowledge.Arg("len.v")], ident) - } - - fn := func(node ast.Node) { - var pkg string - var fun string - - ifstmt := node.(*ast.IfStmt) - if ifstmt.Init != nil { - return - } - if ifstmt.Else != nil { - return - } - if len(ifstmt.Body.List) != 1 { - return - } - condCall, ok := ifstmt.Cond.(*ast.CallExpr) - if !ok { - return - } - - condCallName := code.CallName(pass, condCall) - switch condCallName { - case "strings.HasPrefix": - pkg = "strings" - fun = "HasPrefix" - case "strings.HasSuffix": - pkg = "strings" - fun = "HasSuffix" - case "strings.Contains": - pkg = "strings" - fun = "Contains" - case "bytes.HasPrefix": - pkg = "bytes" - fun = "HasPrefix" - case "bytes.HasSuffix": - pkg = "bytes" - fun = "HasSuffix" - case "bytes.Contains": - pkg = "bytes" - fun = "Contains" - default: - return - } - - assign, ok := ifstmt.Body.List[0].(*ast.AssignStmt) - if !ok { - return - } - if assign.Tok != token.ASSIGN { - return - } - if len(assign.Lhs) != 1 || len(assign.Rhs) != 1 { - return - } - if !sameNonDynamic(condCall.Args[0], assign.Lhs[0]) { - return - } - - switch rhs := assign.Rhs[0].(type) { - case *ast.CallExpr: - if len(rhs.Args) < 2 || !sameNonDynamic(condCall.Args[0], rhs.Args[0]) || !sameNonDynamic(condCall.Args[1], rhs.Args[1]) { - return - } - - rhsName := code.CallName(pass, rhs) - if condCallName == "strings.HasPrefix" && rhsName == "strings.TrimPrefix" || - condCallName == "strings.HasSuffix" && rhsName == "strings.TrimSuffix" || - condCallName == "strings.Contains" && rhsName == "strings.Replace" || - condCallName == "bytes.HasPrefix" && rhsName == "bytes.TrimPrefix" || - condCallName == "bytes.HasSuffix" && rhsName == "bytes.TrimSuffix" || - condCallName == "bytes.Contains" && rhsName == "bytes.Replace" { - report.Report(pass, ifstmt, fmt.Sprintf("should replace this if statement with an unconditional %s", rhsName), report.FilterGenerated()) - } - case *ast.SliceExpr: - slice := rhs - if !ok { - return - } - if slice.Slice3 { - return - } - if !sameNonDynamic(slice.X, condCall.Args[0]) { - return - } - - validateOffset := func(off ast.Expr) bool { - switch off := off.(type) { - case *ast.CallExpr: - return isLenOnIdent(off, condCall.Args[1]) - case *ast.BasicLit: - if pkg != "strings" { - return false - } - if _, ok := condCall.Args[1].(*ast.BasicLit); !ok { - // Only allow manual slicing with an integer - // literal if the second argument to HasPrefix - // was a string literal. - return false - } - s, ok1 := code.ExprToString(pass, condCall.Args[1]) - n, ok2 := code.ExprToInt(pass, off) - if !ok1 || !ok2 || n != int64(len(s)) { - return false - } - return true - default: - return false - } - } - - switch fun { - case "HasPrefix": - // TODO(dh) We could detect a High that is len(s), but another - // rule will already flag that, anyway. - if slice.High != nil { - return - } - if !validateOffset(slice.Low) { - return - } - case "HasSuffix": - if slice.Low != nil { - n, ok := code.ExprToInt(pass, slice.Low) - if !ok || n != 0 { - return - } - } - switch index := slice.High.(type) { - case *ast.BinaryExpr: - if index.Op != token.SUB { - return - } - if !isLenOnIdent(index.X, condCall.Args[0]) { - return - } - if !validateOffset(index.Y) { - return - } - default: - return - } - default: - return - } - - var replacement string - switch fun { - case "HasPrefix": - replacement = "TrimPrefix" - case "HasSuffix": - replacement = "TrimSuffix" - } - report.Report(pass, ifstmt, fmt.Sprintf("should replace this if statement with an unconditional %s.%s", pkg, replacement), - report.ShortRange(), - report.FilterGenerated()) - } - } - code.Preorder(pass, fn, (*ast.IfStmt)(nil)) - return nil, nil -} - -var ( - checkLoopSlideQ = pattern.MustParse(` - (ForStmt - (AssignStmt initvar@(Ident _) _ (IntegerLiteral "0")) - (BinaryExpr initvar "<" limit@(Ident _)) - (IncDecStmt initvar "++") - [(AssignStmt - (IndexExpr slice@(Ident _) initvar) - "=" - (IndexExpr slice (BinaryExpr offset@(Ident _) "+" initvar)))])`) - checkLoopSlideR = pattern.MustParse(` - (CallExpr - (Ident "copy") - [(SliceExpr slice nil limit nil) - (SliceExpr slice offset nil nil)])`) -) - -func CheckLoopSlide(pass *analysis.Pass) (interface{}, error) { - // TODO(dh): detect bs[i+offset] in addition to bs[offset+i] - // TODO(dh): consider merging this function with LintLoopCopy - // TODO(dh): detect length that is an expression, not a variable name - // TODO(dh): support sliding to a different offset than the beginning of the slice - - fn := func(node ast.Node) { - loop := node.(*ast.ForStmt) - m, edits, ok := code.MatchAndEdit(pass, checkLoopSlideQ, checkLoopSlideR, loop) - if !ok { - return - } - typ := pass.TypesInfo.TypeOf(m.State["slice"].(*ast.Ident)) - // The pattern probably needs a core type, but All is fine, too. Either way we only accept slices. - if !typeutil.All(typ, typeutil.IsSlice) { - return - } - - report.Report(pass, loop, "should use copy() instead of loop for sliding slice elements", - report.ShortRange(), - report.FilterGenerated(), - report.Fixes(edit.Fix("use copy() instead of loop", edits...))) - } - code.Preorder(pass, fn, (*ast.ForStmt)(nil)) - return nil, nil -} - -var ( - checkMakeLenCapQ1 = pattern.MustParse(`(CallExpr (Builtin "make") [typ size@(IntegerLiteral "0")])`) - checkMakeLenCapQ2 = pattern.MustParse(`(CallExpr (Builtin "make") [typ size size])`) -) - -func CheckMakeLenCap(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - if pass.Pkg.Path() == "runtime_test" && filepath.Base(pass.Fset.Position(node.Pos()).Filename) == "map_test.go" { - // special case of runtime tests testing map creation - return - } - if m, ok := code.Match(pass, checkMakeLenCapQ1, node); ok { - T := m.State["typ"].(ast.Expr) - size := m.State["size"].(ast.Node) - - if _, ok := typeutil.CoreType(pass.TypesInfo.TypeOf(T)).Underlying().(*types.Chan); ok { - report.Report(pass, size, fmt.Sprintf("should use make(%s) instead", report.Render(pass, T)), report.FilterGenerated()) - } - - } else if m, ok := code.Match(pass, checkMakeLenCapQ2, node); ok { - // TODO(dh): don't consider sizes identical if they're - // dynamic. for example: make(T, <-ch, <-ch). - T := m.State["typ"].(ast.Expr) - size := m.State["size"].(ast.Node) - report.Report(pass, size, - fmt.Sprintf("should use make(%s, %s) instead", report.Render(pass, T), report.Render(pass, size)), - report.FilterGenerated()) - } - } - code.Preorder(pass, fn, (*ast.CallExpr)(nil)) - return nil, nil -} - -var ( - checkAssertNotNilFn1Q = pattern.MustParse(` - (IfStmt - (AssignStmt [(Ident "_") ok@(Object _)] _ [(TypeAssertExpr assert@(Object _) _)]) - (Or - (BinaryExpr ok "&&" (BinaryExpr assert "!=" (Builtin "nil"))) - (BinaryExpr (BinaryExpr assert "!=" (Builtin "nil")) "&&" ok)) - _ - _)`) - checkAssertNotNilFn2Q = pattern.MustParse(` - (IfStmt - nil - (BinaryExpr lhs@(Object _) "!=" (Builtin "nil")) - [ - ifstmt@(IfStmt - (AssignStmt [(Ident "_") ok@(Object _)] _ [(TypeAssertExpr lhs _)]) - ok - _ - nil) - ] - nil)`) -) - -func CheckAssertNotNil(pass *analysis.Pass) (interface{}, error) { - fn1 := func(node ast.Node) { - m, ok := code.Match(pass, checkAssertNotNilFn1Q, node) - if !ok { - return - } - assert := m.State["assert"].(types.Object) - assign := m.State["ok"].(types.Object) - report.Report(pass, node, fmt.Sprintf("when %s is true, %s can't be nil", assign.Name(), assert.Name()), - report.ShortRange(), - report.FilterGenerated()) - } - fn2 := func(node ast.Node) { - m, ok := code.Match(pass, checkAssertNotNilFn2Q, node) - if !ok { - return - } - ifstmt := m.State["ifstmt"].(*ast.IfStmt) - lhs := m.State["lhs"].(types.Object) - assignIdent := m.State["ok"].(types.Object) - report.Report(pass, ifstmt, fmt.Sprintf("when %s is true, %s can't be nil", assignIdent.Name(), lhs.Name()), - report.ShortRange(), - report.FilterGenerated()) - } - // OPT(dh): merge fn1 and fn2 - code.Preorder(pass, fn1, (*ast.IfStmt)(nil)) - code.Preorder(pass, fn2, (*ast.IfStmt)(nil)) - return nil, nil -} - -func CheckDeclareAssign(pass *analysis.Pass) (interface{}, error) { - hasMultipleAssignments := func(root ast.Node, ident *ast.Ident) bool { - num := 0 - ast.Inspect(root, func(node ast.Node) bool { - if num >= 2 { - return false - } - assign, ok := node.(*ast.AssignStmt) - if !ok { - return true - } - for _, lhs := range assign.Lhs { - if oident, ok := lhs.(*ast.Ident); ok { - if pass.TypesInfo.ObjectOf(oident) == pass.TypesInfo.ObjectOf(ident) { - num++ - } - } - } - - return true - }) - return num >= 2 - } - fn := func(node ast.Node) { - block := node.(*ast.BlockStmt) - if len(block.List) < 2 { - return - } - for i, stmt := range block.List[:len(block.List)-1] { - _ = i - decl, ok := stmt.(*ast.DeclStmt) - if !ok { - continue - } - gdecl, ok := decl.Decl.(*ast.GenDecl) - if !ok || gdecl.Tok != token.VAR || len(gdecl.Specs) != 1 { - continue - } - vspec, ok := gdecl.Specs[0].(*ast.ValueSpec) - if !ok || len(vspec.Names) != 1 || len(vspec.Values) != 0 { - continue - } - - assign, ok := block.List[i+1].(*ast.AssignStmt) - if !ok || assign.Tok != token.ASSIGN { - continue - } - if len(assign.Lhs) != 1 || len(assign.Rhs) != 1 { - continue - } - ident, ok := assign.Lhs[0].(*ast.Ident) - if !ok { - continue - } - if pass.TypesInfo.ObjectOf(vspec.Names[0]) != pass.TypesInfo.ObjectOf(ident) { - continue - } - - if refersTo(pass, assign.Rhs[0], pass.TypesInfo.ObjectOf(ident)) { - continue - } - if hasMultipleAssignments(block, ident) { - continue - } - - r := &ast.GenDecl{ - Specs: []ast.Spec{ - &ast.ValueSpec{ - Names: vspec.Names, - Values: []ast.Expr{assign.Rhs[0]}, - Type: vspec.Type, - }, - }, - Tok: gdecl.Tok, - } - report.Report(pass, decl, "should merge variable declaration with assignment on next line", - report.FilterGenerated(), - report.Fixes(edit.Fix("merge declaration with assignment", edit.ReplaceWithNode(pass.Fset, edit.Range{decl.Pos(), assign.End()}, r)))) - } - } - code.Preorder(pass, fn, (*ast.BlockStmt)(nil)) - return nil, nil -} - -func CheckRedundantBreak(pass *analysis.Pass) (interface{}, error) { - fn1 := func(node ast.Node) { - clause := node.(*ast.CaseClause) - if len(clause.Body) < 2 { - return - } - branch, ok := clause.Body[len(clause.Body)-1].(*ast.BranchStmt) - if !ok || branch.Tok != token.BREAK || branch.Label != nil { - return - } - report.Report(pass, branch, "redundant break statement", report.FilterGenerated()) - } - fn2 := func(node ast.Node) { - var ret *ast.FieldList - var body *ast.BlockStmt - switch x := node.(type) { - case *ast.FuncDecl: - ret = x.Type.Results - body = x.Body - case *ast.FuncLit: - ret = x.Type.Results - body = x.Body - default: - lint.ExhaustiveTypeSwitch(node) - } - // if the func has results, a return can't be redundant. - // similarly, if there are no statements, there can be - // no return. - if ret != nil || body == nil || len(body.List) < 1 { - return - } - rst, ok := body.List[len(body.List)-1].(*ast.ReturnStmt) - if !ok { - return - } - // we don't need to check rst.Results as we already - // checked x.Type.Results to be nil. - report.Report(pass, rst, "redundant return statement", report.FilterGenerated()) - } - code.Preorder(pass, fn1, (*ast.CaseClause)(nil)) - code.Preorder(pass, fn2, (*ast.FuncDecl)(nil), (*ast.FuncLit)(nil)) - return nil, nil -} - -func isFormatter(T types.Type, msCache *typeutil.MethodSetCache) bool { - // TODO(dh): this function also exists in staticcheck/lint.go – deduplicate. - - ms := msCache.MethodSet(T) - sel := ms.Lookup(nil, "Format") - if sel == nil { - return false - } - fn, ok := sel.Obj().(*types.Func) - if !ok { - // should be unreachable - return false - } - sig := fn.Type().(*types.Signature) - if sig.Params().Len() != 2 { - return false - } - // TODO(dh): check the types of the arguments for more - // precision - if sig.Results().Len() != 0 { - return false - } - return true -} - -var checkRedundantSprintfQ = pattern.MustParse(`(CallExpr (Symbol "fmt.Sprintf") [format arg])`) - -func CheckRedundantSprintf(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - m, ok := code.Match(pass, checkRedundantSprintfQ, node) - if !ok { - return - } - - format := m.State["format"].(ast.Expr) - arg := m.State["arg"].(ast.Expr) - // TODO(dh): should we really support named constants here? - // shouldn't we only look for string literals? to avoid false - // positives via build tags? - if s, ok := code.ExprToString(pass, format); !ok || s != "%s" { - return - } - typ := pass.TypesInfo.TypeOf(arg) - if typeparams.IsTypeParam(typ) { - return - } - irpkg := pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg - - if types.TypeString(typ, nil) == "reflect.Value" { - // printing with %s produces output different from using - // the String method - return - } - - if isFormatter(typ, &irpkg.Prog.MethodSets) { - // the type may choose to handle %s in arbitrary ways - return - } - - if types.Implements(typ, knowledge.Interfaces["fmt.Stringer"]) { - replacement := &ast.CallExpr{ - Fun: &ast.SelectorExpr{ - X: arg, - Sel: &ast.Ident{Name: "String"}, - }, - } - report.Report(pass, node, "should use String() instead of fmt.Sprintf", - report.Fixes(edit.Fix("replace with call to String method", edit.ReplaceWithNode(pass.Fset, node, replacement)))) - } else if typ == types.Universe.Lookup("string").Type() { - report.Report(pass, node, "the argument is already a string, there's no need to use fmt.Sprintf", - report.FilterGenerated(), - report.Fixes(edit.Fix("remove unnecessary call to fmt.Sprintf", edit.ReplaceWithNode(pass.Fset, node, arg)))) - } else if typ.Underlying() == types.Universe.Lookup("string").Type() { - replacement := &ast.CallExpr{ - Fun: &ast.Ident{Name: "string"}, - Args: []ast.Expr{arg}, - } - report.Report(pass, node, "the argument's underlying type is a string, should use a simple conversion instead of fmt.Sprintf", - report.FilterGenerated(), - report.Fixes(edit.Fix("replace with conversion to string", edit.ReplaceWithNode(pass.Fset, node, replacement)))) - } else if slice, ok := typ.Underlying().(*types.Slice); ok && slice.Elem() == types.Universe.Lookup("byte").Type() { - // Note that we check slice.Elem(), not slice.Elem().Underlying, because of https://github.com/golang/go/issues/23536 - replacement := &ast.CallExpr{ - Fun: &ast.Ident{Name: "string"}, - Args: []ast.Expr{arg}, - } - report.Report(pass, node, "the argument's underlying type is a slice of bytes, should use a simple conversion instead of fmt.Sprintf", - report.FilterGenerated(), - report.Fixes(edit.Fix("replace with conversion to string", edit.ReplaceWithNode(pass.Fset, node, replacement)))) - } - - } - code.Preorder(pass, fn, (*ast.CallExpr)(nil)) - return nil, nil -} - -var ( - checkErrorsNewSprintfQ = pattern.MustParse(`(CallExpr (Symbol "errors.New") [(CallExpr (Symbol "fmt.Sprintf") args)])`) - checkErrorsNewSprintfR = pattern.MustParse(`(CallExpr (SelectorExpr (Ident "fmt") (Ident "Errorf")) args)`) -) - -func CheckErrorsNewSprintf(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - if _, edits, ok := code.MatchAndEdit(pass, checkErrorsNewSprintfQ, checkErrorsNewSprintfR, node); ok { - // TODO(dh): the suggested fix may leave an unused import behind - report.Report(pass, node, "should use fmt.Errorf(...) instead of errors.New(fmt.Sprintf(...))", - report.FilterGenerated(), - report.Fixes(edit.Fix("use fmt.Errorf", edits...))) - } - } - code.Preorder(pass, fn, (*ast.CallExpr)(nil)) - return nil, nil -} - -func CheckRangeStringRunes(pass *analysis.Pass) (interface{}, error) { - return sharedcheck.CheckRangeStringRunes(pass) -} - -var checkNilCheckAroundRangeQ = pattern.MustParse(` - (IfStmt - nil - (BinaryExpr x@(Object _) "!=" (Builtin "nil")) - [(RangeStmt _ _ _ x _)] - nil)`) - -func CheckNilCheckAroundRange(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - m, ok := code.Match(pass, checkNilCheckAroundRangeQ, node) - if !ok { - return - } - ok = typeutil.All(m.State["x"].(types.Object).Type(), func(term *types.Term) bool { - switch term.Type().Underlying().(type) { - case *types.Slice, *types.Map: - return true - case *types.TypeParam, *types.Chan, *types.Pointer: - return false - default: - lint.ExhaustiveTypeSwitch(term.Type().Underlying()) - return false - } - }) - if !ok { - return - } - report.Report(pass, node, "unnecessary nil check around range", report.ShortRange(), report.FilterGenerated()) - } - code.Preorder(pass, fn, (*ast.IfStmt)(nil)) - return nil, nil -} - -func isPermissibleSort(pass *analysis.Pass, node ast.Node) bool { - call := node.(*ast.CallExpr) - typeconv, ok := call.Args[0].(*ast.CallExpr) - if !ok { - return true - } - - sel, ok := typeconv.Fun.(*ast.SelectorExpr) - if !ok { - return true - } - name := code.SelectorName(pass, sel) - switch name { - case "sort.IntSlice", "sort.Float64Slice", "sort.StringSlice": - default: - return true - } - - return false -} - -func CheckSortHelpers(pass *analysis.Pass) (interface{}, error) { - type Error struct { - node ast.Node - msg string - } - var allErrors []Error - fn := func(node ast.Node) { - var body *ast.BlockStmt - switch node := node.(type) { - case *ast.FuncLit: - body = node.Body - case *ast.FuncDecl: - body = node.Body - default: - lint.ExhaustiveTypeSwitch(node) - } - if body == nil { - return - } - - var errors []Error - permissible := false - fnSorts := func(node ast.Node) bool { - if permissible { - return false - } - if !code.IsCallTo(pass, node, "sort.Sort") { - return true - } - if isPermissibleSort(pass, node) { - permissible = true - return false - } - call := node.(*ast.CallExpr) - // isPermissibleSort guarantees that this type assertion will succeed - typeconv := call.Args[knowledge.Arg("sort.Sort.data")].(*ast.CallExpr) - sel := typeconv.Fun.(*ast.SelectorExpr) - name := code.SelectorName(pass, sel) - - switch name { - case "sort.IntSlice": - errors = append(errors, Error{node, "should use sort.Ints(...) instead of sort.Sort(sort.IntSlice(...))"}) - case "sort.Float64Slice": - errors = append(errors, Error{node, "should use sort.Float64s(...) instead of sort.Sort(sort.Float64Slice(...))"}) - case "sort.StringSlice": - errors = append(errors, Error{node, "should use sort.Strings(...) instead of sort.Sort(sort.StringSlice(...))"}) - } - return true - } - ast.Inspect(body, fnSorts) - - if permissible { - return - } - allErrors = append(allErrors, errors...) - } - code.Preorder(pass, fn, (*ast.FuncLit)(nil), (*ast.FuncDecl)(nil)) - sort.Slice(allErrors, func(i, j int) bool { - return allErrors[i].node.Pos() < allErrors[j].node.Pos() - }) - var prev token.Pos - for _, err := range allErrors { - if err.node.Pos() == prev { - continue - } - prev = err.node.Pos() - report.Report(pass, err.node, err.msg, report.FilterGenerated()) - } - return nil, nil -} - -var checkGuardedDeleteQ = pattern.MustParse(` - (IfStmt - (AssignStmt - [(Ident "_") ok@(Ident _)] - ":=" - (IndexExpr m key)) - ok - [call@(CallExpr (Builtin "delete") [m key])] - nil)`) - -func CheckGuardedDelete(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - if m, ok := code.Match(pass, checkGuardedDeleteQ, node); ok { - report.Report(pass, node, "unnecessary guard around call to delete", - report.ShortRange(), - report.FilterGenerated(), - report.Fixes(edit.Fix("remove guard", edit.ReplaceWithNode(pass.Fset, node, m.State["call"].(ast.Node))))) - } - } - - code.Preorder(pass, fn, (*ast.IfStmt)(nil)) - return nil, nil -} - -var ( - checkSimplifyTypeSwitchQ = pattern.MustParse(` - (TypeSwitchStmt - nil - expr@(TypeAssertExpr ident@(Ident _) _) - body)`) - checkSimplifyTypeSwitchR = pattern.MustParse(`(AssignStmt ident ":=" expr)`) -) - -func CheckSimplifyTypeSwitch(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - m, ok := code.Match(pass, checkSimplifyTypeSwitchQ, node) - if !ok { - return - } - stmt := node.(*ast.TypeSwitchStmt) - expr := m.State["expr"].(ast.Node) - ident := m.State["ident"].(*ast.Ident) - - x := pass.TypesInfo.ObjectOf(ident) - var allOffenders []*ast.TypeAssertExpr - canSuggestFix := true - for _, clause := range stmt.Body.List { - clause := clause.(*ast.CaseClause) - if len(clause.List) != 1 { - continue - } - hasUnrelatedAssertion := false - var offenders []*ast.TypeAssertExpr - ast.Inspect(clause, func(node ast.Node) bool { - assert2, ok := node.(*ast.TypeAssertExpr) - if !ok { - return true - } - ident, ok := assert2.X.(*ast.Ident) - if !ok { - hasUnrelatedAssertion = true - return false - } - if pass.TypesInfo.ObjectOf(ident) != x { - hasUnrelatedAssertion = true - return false - } - - if !types.Identical(pass.TypesInfo.TypeOf(clause.List[0]), pass.TypesInfo.TypeOf(assert2.Type)) { - hasUnrelatedAssertion = true - return false - } - offenders = append(offenders, assert2) - return true - }) - if !hasUnrelatedAssertion { - // don't flag cases that have other type assertions - // unrelated to the one in the case clause. often - // times, this is done for symmetry, when two - // different values have to be asserted to the same - // type. - allOffenders = append(allOffenders, offenders...) - } - canSuggestFix = canSuggestFix && !hasUnrelatedAssertion - } - if len(allOffenders) != 0 { - var opts []report.Option - for _, offender := range allOffenders { - opts = append(opts, report.Related(offender, "could eliminate this type assertion")) - } - opts = append(opts, report.FilterGenerated()) - - msg := fmt.Sprintf("assigning the result of this type assertion to a variable (switch %s := %s.(type)) could eliminate type assertions in switch cases", - report.Render(pass, ident), report.Render(pass, ident)) - if canSuggestFix { - var edits []analysis.TextEdit - edits = append(edits, edit.ReplaceWithPattern(pass.Fset, expr, checkSimplifyTypeSwitchR, m.State)) - for _, offender := range allOffenders { - edits = append(edits, edit.ReplaceWithNode(pass.Fset, offender, offender.X)) - } - opts = append(opts, report.Fixes(edit.Fix("simplify type switch", edits...))) - report.Report(pass, expr, msg, opts...) - } else { - report.Report(pass, expr, msg, opts...) - } - } - } - code.Preorder(pass, fn, (*ast.TypeSwitchStmt)(nil)) - return nil, nil -} - -func CheckRedundantCanonicalHeaderKey(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - call := node.(*ast.CallExpr) - callName := code.CallName(pass, call) - switch callName { - case "(net/http.Header).Add", "(net/http.Header).Del", "(net/http.Header).Get", "(net/http.Header).Set": - default: - return - } - - if !code.IsCallTo(pass, call.Args[0], "net/http.CanonicalHeaderKey") { - return - } - - report.Report(pass, call, - fmt.Sprintf("calling net/http.CanonicalHeaderKey on the 'key' argument of %s is redundant", callName), - report.FilterGenerated(), - report.Fixes(edit.Fix("remove call to CanonicalHeaderKey", edit.ReplaceWithNode(pass.Fset, call.Args[0], call.Args[0].(*ast.CallExpr).Args[0])))) - } - code.Preorder(pass, fn, (*ast.CallExpr)(nil)) - return nil, nil -} - -var checkUnnecessaryGuardQ = pattern.MustParse(` - (Or - (IfStmt - (AssignStmt [(Ident "_") ok@(Ident _)] ":=" indexexpr@(IndexExpr _ _)) - ok - set@(AssignStmt indexexpr "=" (CallExpr (Builtin "append") indexexpr:values)) - (AssignStmt indexexpr "=" (CompositeLit _ values))) - (IfStmt - (AssignStmt [(Ident "_") ok] ":=" indexexpr@(IndexExpr _ _)) - ok - set@(AssignStmt indexexpr "+=" value) - (AssignStmt indexexpr "=" value)) - (IfStmt - (AssignStmt [(Ident "_") ok] ":=" indexexpr@(IndexExpr _ _)) - ok - set@(IncDecStmt indexexpr "++") - (AssignStmt indexexpr "=" (IntegerLiteral "1"))))`) - -func CheckUnnecessaryGuard(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - if m, ok := code.Match(pass, checkUnnecessaryGuardQ, node); ok { - if code.MayHaveSideEffects(pass, m.State["indexexpr"].(ast.Expr), nil) { - return - } - report.Report(pass, node, "unnecessary guard around map access", - report.ShortRange(), - report.Fixes(edit.Fix("simplify map access", edit.ReplaceWithNode(pass.Fset, node, m.State["set"].(ast.Node))))) - } - } - code.Preorder(pass, fn, (*ast.IfStmt)(nil)) - return nil, nil -} - -var ( - checkElaborateSleepQ = pattern.MustParse(`(SelectStmt (CommClause (UnaryExpr "<-" (CallExpr (Symbol "time.After") [arg])) body))`) - checkElaborateSleepR = pattern.MustParse(`(CallExpr (SelectorExpr (Ident "time") (Ident "Sleep")) [arg])`) -) - -func CheckElaborateSleep(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - if m, ok := code.Match(pass, checkElaborateSleepQ, node); ok { - if body, ok := m.State["body"].([]ast.Stmt); ok && len(body) == 0 { - report.Report(pass, node, "should use time.Sleep instead of elaborate way of sleeping", - report.ShortRange(), - report.FilterGenerated(), - report.Fixes(edit.Fix("Use time.Sleep", edit.ReplaceWithPattern(pass.Fset, node, checkElaborateSleepR, m.State)))) - } else { - // TODO(dh): we could make a suggested fix if the body - // doesn't declare or shadow any identifiers - report.Report(pass, node, "should use time.Sleep instead of elaborate way of sleeping", - report.ShortRange(), - report.FilterGenerated()) - } - } - } - code.Preorder(pass, fn, (*ast.SelectStmt)(nil)) - return nil, nil -} - -var ( - checkPrintSprintQ = pattern.MustParse(` - (Or - (CallExpr - fn@(Or - (Symbol "fmt.Print") - (Symbol "fmt.Sprint") - (Symbol "fmt.Println") - (Symbol "fmt.Sprintln")) - [(CallExpr (Symbol "fmt.Sprintf") f:_)]) - (CallExpr - fn@(Or - (Symbol "fmt.Fprint") - (Symbol "fmt.Fprintln")) - [_ (CallExpr (Symbol "fmt.Sprintf") f:_)]))`) - - checkTestingErrorSprintfQ = pattern.MustParse(` - (CallExpr - sel@(SelectorExpr - recv - (Ident - name@(Or - "Error" - "Fatal" - "Fatalln" - "Log" - "Panic" - "Panicln" - "Print" - "Println" - "Skip"))) - [(CallExpr (Symbol "fmt.Sprintf") args)])`) - - checkLogSprintfQ = pattern.MustParse(` - (CallExpr - (Symbol - (Or - "log.Fatal" - "log.Fatalln" - "log.Panic" - "log.Panicln" - "log.Print" - "log.Println")) - [(CallExpr (Symbol "fmt.Sprintf") args)])`) - - checkSprintfMapping = map[string]struct { - recv string - alternative string - }{ - "(*testing.common).Error": {"(*testing.common)", "Errorf"}, - "(testing.TB).Error": {"(testing.TB)", "Errorf"}, - "(*testing.common).Fatal": {"(*testing.common)", "Fatalf"}, - "(testing.TB).Fatal": {"(testing.TB)", "Fatalf"}, - "(*testing.common).Log": {"(*testing.common)", "Logf"}, - "(testing.TB).Log": {"(testing.TB)", "Logf"}, - "(*testing.common).Skip": {"(*testing.common)", "Skipf"}, - "(testing.TB).Skip": {"(testing.TB)", "Skipf"}, - "(*log.Logger).Fatal": {"(*log.Logger)", "Fatalf"}, - "(*log.Logger).Fatalln": {"(*log.Logger)", "Fatalf"}, - "(*log.Logger).Panic": {"(*log.Logger)", "Panicf"}, - "(*log.Logger).Panicln": {"(*log.Logger)", "Panicf"}, - "(*log.Logger).Print": {"(*log.Logger)", "Printf"}, - "(*log.Logger).Println": {"(*log.Logger)", "Printf"}, - "log.Fatal": {"", "log.Fatalf"}, - "log.Fatalln": {"", "log.Fatalf"}, - "log.Panic": {"", "log.Panicf"}, - "log.Panicln": {"", "log.Panicf"}, - "log.Print": {"", "log.Printf"}, - "log.Println": {"", "log.Printf"}, - } -) - -func CheckPrintSprintf(pass *analysis.Pass) (interface{}, error) { - fmtPrintf := func(node ast.Node) { - m, ok := code.Match(pass, checkPrintSprintQ, node) - if !ok { - return - } - - name := m.State["fn"].(*types.Func).Name() - var msg string - switch name { - case "Print", "Fprint", "Sprint": - newname := name + "f" - msg = fmt.Sprintf("should use fmt.%s instead of fmt.%s(fmt.Sprintf(...))", newname, name) - case "Println", "Fprintln", "Sprintln": - if _, ok := m.State["f"].(*ast.BasicLit); !ok { - // This may be an instance of - // fmt.Println(fmt.Sprintf(arg, ...)) where arg is an - // externally provided format string and the caller - // cannot guarantee that the format string ends with a - // newline. - return - } - newname := name[:len(name)-2] + "f" - msg = fmt.Sprintf("should use fmt.%s instead of fmt.%s(fmt.Sprintf(...)) (but don't forget the newline)", newname, name) - } - report.Report(pass, node, msg, - report.FilterGenerated()) - } - - methSprintf := func(node ast.Node) { - m, ok := code.Match(pass, checkTestingErrorSprintfQ, node) - if !ok { - return - } - mapped, ok := checkSprintfMapping[code.CallName(pass, node.(*ast.CallExpr))] - if !ok { - return - } - - // Ensure that Errorf/Fatalf refer to the right method - recvTV, ok := pass.TypesInfo.Types[m.State["recv"].(ast.Expr)] - if !ok { - return - } - obj, _, _ := types.LookupFieldOrMethod(recvTV.Type, recvTV.Addressable(), nil, mapped.alternative) - f, ok := obj.(*types.Func) - if !ok { - return - } - if typeutil.FuncName(f) != mapped.recv+"."+mapped.alternative { - return - } - - alt := &ast.SelectorExpr{ - X: m.State["recv"].(ast.Expr), - Sel: &ast.Ident{Name: mapped.alternative}, - } - report.Report(pass, node, fmt.Sprintf("should use %s(...) instead of %s(fmt.Sprintf(...))", report.Render(pass, alt), report.Render(pass, m.State["sel"].(*ast.SelectorExpr)))) - } - - pkgSprintf := func(node ast.Node) { - _, ok := code.Match(pass, checkLogSprintfQ, node) - if !ok { - return - } - callName := code.CallName(pass, node.(*ast.CallExpr)) - mapped, ok := checkSprintfMapping[callName] - if !ok { - return - } - report.Report(pass, node, fmt.Sprintf("should use %s(...) instead of %s(fmt.Sprintf(...))", mapped.alternative, callName)) - } - - fn := func(node ast.Node) { - fmtPrintf(node) - // TODO(dh): add suggested fixes - methSprintf(node) - pkgSprintf(node) - } - code.Preorder(pass, fn, (*ast.CallExpr)(nil)) - return nil, nil -} - -var checkSprintLiteralQ = pattern.MustParse(` - (CallExpr - fn@(Or - (Symbol "fmt.Sprint") - (Symbol "fmt.Sprintf")) - [lit@(BasicLit "STRING" _)])`) - -func CheckSprintLiteral(pass *analysis.Pass) (interface{}, error) { - // We only flag calls with string literals, not expressions of - // type string, because some people use fmt.Sprint(s) as a pattern - // for copying strings, which may be useful when extracting a small - // substring from a large string. - fn := func(node ast.Node) { - m, ok := code.Match(pass, checkSprintLiteralQ, node) - if !ok { - return - } - callee := m.State["fn"].(*types.Func) - lit := m.State["lit"].(*ast.BasicLit) - if callee.Name() == "Sprintf" { - if strings.ContainsRune(lit.Value, '%') { - // This might be a format string - return - } - } - report.Report(pass, node, fmt.Sprintf("unnecessary use of fmt.%s", callee.Name()), - report.FilterGenerated(), - report.Fixes(edit.Fix("Replace with string literal", edit.ReplaceWithNode(pass.Fset, node, lit)))) - } - code.Preorder(pass, fn, (*ast.CallExpr)(nil)) - return nil, nil -} - -func CheckSameTypeTypeAssertion(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - expr := node.(*ast.TypeAssertExpr) - if expr.Type == nil { - // skip type switches - // - // TODO(dh): we could flag type switches, too, when a case - // statement has the same type as expr.X – however, - // depending on the location of that case, it might behave - // identically to a default branch. we need to think - // carefully about the instances we want to flag. We also - // have to take nil interface values into consideration. - // - // It might make more sense to extend SA4020 to handle - // this. - return - } - t1 := pass.TypesInfo.TypeOf(expr.Type) - t2 := pass.TypesInfo.TypeOf(expr.X) - if types.IsInterface(t1) && types.Identical(t1, t2) { - report.Report(pass, expr, - fmt.Sprintf("type assertion to the same type: %s already has type %s", report.Render(pass, expr.X), report.Render(pass, expr.Type)), - report.FilterGenerated()) - } - } - - // TODO(dh): add suggested fixes. we need different fixes depending on the context: - // - assignment with 1 or 2 lhs - // - assignment to blank identifiers (as the first, second or both lhs) - // - initializers in if statements, with the same variations as above - - code.Preorder(pass, fn, (*ast.TypeAssertExpr)(nil)) - return nil, nil -} diff --git a/tools/vendor/honnef.co/go/tools/simple/s1000/s1000.go b/tools/vendor/honnef.co/go/tools/simple/s1000/s1000.go new file mode 100644 index 0000000000..c5b9a1408f --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/simple/s1000/s1000.go @@ -0,0 +1,71 @@ +package s1000 + +import ( + "go/ast" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "S1000", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Use plain channel send or receive instead of single-case select`, + Text: `Select statements with a single case can be replaced with a simple +send or receive.`, + Before: ` +select { +case x := <-ch: + fmt.Println(x) +}`, + After: ` +x := <-ch +fmt.Println(x) +`, + Since: "2017.1", + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var ( + checkSingleCaseSelectQ1 = pattern.MustParse(` + (ForStmt + nil nil nil + select@(SelectStmt + (CommClause + (Or + (UnaryExpr "<-" _) + (AssignStmt _ _ (UnaryExpr "<-" _))) + _)))`) + checkSingleCaseSelectQ2 = pattern.MustParse(`(SelectStmt (CommClause _ _))`) +) + +func run(pass *analysis.Pass) (interface{}, error) { + seen := map[ast.Node]struct{}{} + fn := func(node ast.Node) { + if m, ok := code.Match(pass, checkSingleCaseSelectQ1, node); ok { + seen[m.State["select"].(ast.Node)] = struct{}{} + report.Report(pass, node, "should use for range instead of for { select {} }", report.FilterGenerated()) + } else if _, ok := code.Match(pass, checkSingleCaseSelectQ2, node); ok { + if _, ok := seen[node]; !ok { + report.Report(pass, node, "should use a simple channel send/receive instead of select with a single case", + report.ShortRange(), + report.FilterGenerated()) + } + } + } + code.Preorder(pass, fn, (*ast.ForStmt)(nil), (*ast.SelectStmt)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/simple/s1001/s1001.go b/tools/vendor/honnef.co/go/tools/simple/s1001/s1001.go new file mode 100644 index 0000000000..889227cd80 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/simple/s1001/s1001.go @@ -0,0 +1,197 @@ +package s1001 + +import ( + "fmt" + "go/ast" + "go/token" + "go/types" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/edit" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "S1001", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Replace for loop with call to copy`, + Text: ` +Use \'copy()\' for copying elements from one slice to another. For +arrays of identical size, you can use simple assignment.`, + Before: ` +for i, x := range src { + dst[i] = x +}`, + After: `copy(dst, src)`, + Since: "2017.1", + // MergeIfAll because the types of src and dst might be different under different build tags. + // You shouldn't write code like that… + MergeIf: lint.MergeIfAll, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var ( + checkLoopCopyQ = pattern.MustParse(` + (Or + (RangeStmt + key@(Ident _) value@(Ident _) ":=" src + [(AssignStmt (IndexExpr dst key) "=" value)]) + (RangeStmt + key@(Ident _) nil ":=" src + [(AssignStmt (IndexExpr dst key) "=" (IndexExpr src key))]) + (ForStmt + (AssignStmt key@(Ident _) ":=" (IntegerLiteral "0")) + (BinaryExpr key "<" (CallExpr (Symbol "len") [src])) + (IncDecStmt key "++") + [(AssignStmt (IndexExpr dst key) "=" (IndexExpr src key))]))`) +) + +func run(pass *analysis.Pass) (interface{}, error) { + // TODO revisit once range doesn't require a structural type + + isInvariant := func(k, v types.Object, node ast.Expr) bool { + if code.MayHaveSideEffects(pass, node, nil) { + return false + } + invariant := true + ast.Inspect(node, func(node ast.Node) bool { + if node, ok := node.(*ast.Ident); ok { + obj := pass.TypesInfo.ObjectOf(node) + if obj == k || obj == v { + // don't allow loop bodies like 'a[i][i] = v' + invariant = false + return false + } + } + return true + }) + return invariant + } + + var elType func(T types.Type) (el types.Type, isArray bool, isArrayPointer bool, ok bool) + elType = func(T types.Type) (el types.Type, isArray bool, isArrayPointer bool, ok bool) { + switch typ := T.Underlying().(type) { + case *types.Slice: + return typ.Elem(), false, false, true + case *types.Array: + return typ.Elem(), true, false, true + case *types.Pointer: + el, isArray, _, ok = elType(typ.Elem()) + return el, isArray, true, ok + default: + return nil, false, false, false + } + } + + fn := func(node ast.Node) { + m, ok := code.Match(pass, checkLoopCopyQ, node) + if !ok { + return + } + + src := m.State["src"].(ast.Expr) + dst := m.State["dst"].(ast.Expr) + + k := pass.TypesInfo.ObjectOf(m.State["key"].(*ast.Ident)) + var v types.Object + if value, ok := m.State["value"]; ok { + v = pass.TypesInfo.ObjectOf(value.(*ast.Ident)) + } + if !isInvariant(k, v, dst) { + return + } + if !isInvariant(k, v, src) { + // For example: 'for i := range foo()' + return + } + + Tsrc := pass.TypesInfo.TypeOf(src) + Tdst := pass.TypesInfo.TypeOf(dst) + TsrcElem, TsrcArray, TsrcPointer, ok := elType(Tsrc) + if !ok { + return + } + if TsrcPointer { + Tsrc = Tsrc.Underlying().(*types.Pointer).Elem() + } + TdstElem, TdstArray, TdstPointer, ok := elType(Tdst) + if !ok { + return + } + if TdstPointer { + Tdst = Tdst.Underlying().(*types.Pointer).Elem() + } + + if !types.Identical(TsrcElem, TdstElem) { + return + } + + if TsrcArray && TdstArray && types.Identical(Tsrc, Tdst) { + if TsrcPointer { + src = &ast.StarExpr{ + X: src, + } + } + if TdstPointer { + dst = &ast.StarExpr{ + X: dst, + } + } + r := &ast.AssignStmt{ + Lhs: []ast.Expr{dst}, + Rhs: []ast.Expr{src}, + Tok: token.ASSIGN, + } + + report.Report(pass, node, "should copy arrays using assignment instead of using a loop", + report.FilterGenerated(), + report.ShortRange(), + report.Fixes(edit.Fix("replace loop with assignment", edit.ReplaceWithNode(pass.Fset, node, r)))) + } else { + tv, err := types.Eval(pass.Fset, pass.Pkg, node.Pos(), "copy") + if err == nil && tv.IsBuiltin() { + to := "to" + from := "from" + src := m.State["src"].(ast.Expr) + if TsrcArray { + from = "from[:]" + src = &ast.SliceExpr{ + X: src, + } + } + dst := m.State["dst"].(ast.Expr) + if TdstArray { + to = "to[:]" + dst = &ast.SliceExpr{ + X: dst, + } + } + + r := &ast.CallExpr{ + Fun: &ast.Ident{Name: "copy"}, + Args: []ast.Expr{dst, src}, + } + opts := []report.Option{ + report.ShortRange(), + report.FilterGenerated(), + report.Fixes(edit.Fix("replace loop with call to copy()", edit.ReplaceWithNode(pass.Fset, node, r))), + } + report.Report(pass, node, fmt.Sprintf("should use copy(%s, %s) instead of a loop", to, from), opts...) + } + } + } + code.Preorder(pass, fn, (*ast.ForStmt)(nil), (*ast.RangeStmt)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/simple/s1002/s1002.go b/tools/vendor/honnef.co/go/tools/simple/s1002/s1002.go new file mode 100644 index 0000000000..8cc37fd2ac --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/simple/s1002/s1002.go @@ -0,0 +1,88 @@ +package s1002 + +import ( + "fmt" + "go/ast" + "go/token" + "go/types" + "strings" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/edit" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/types/typeutil" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "S1002", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Omit comparison with boolean constant`, + Before: `if x == true {}`, + After: `if x {}`, + Since: "2017.1", + // MergeIfAll because 'true' might not be the builtin constant under all build tags. + // You shouldn't write code like that… + MergeIf: lint.MergeIfAll, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + if code.IsInTest(pass, node) { + return + } + + expr := node.(*ast.BinaryExpr) + if expr.Op != token.EQL && expr.Op != token.NEQ { + return + } + x := code.IsBoolConst(pass, expr.X) + y := code.IsBoolConst(pass, expr.Y) + if !x && !y { + return + } + var other ast.Expr + var val bool + if x { + val = code.BoolConst(pass, expr.X) + other = expr.Y + } else { + val = code.BoolConst(pass, expr.Y) + other = expr.X + } + + ok := typeutil.All(pass.TypesInfo.TypeOf(other), func(term *types.Term) bool { + basic, ok := term.Type().Underlying().(*types.Basic) + return ok && basic.Kind() == types.Bool + }) + if !ok { + return + } + op := "" + if (expr.Op == token.EQL && !val) || (expr.Op == token.NEQ && val) { + op = "!" + } + r := op + report.Render(pass, other) + l1 := len(r) + r = strings.TrimLeft(r, "!") + if (l1-len(r))%2 == 1 { + r = "!" + r + } + report.Report(pass, expr, fmt.Sprintf("should omit comparison to bool constant, can be simplified to %s", r), + report.FilterGenerated(), + report.Fixes(edit.Fix("simplify bool comparison", edit.ReplaceWithString(expr, r)))) + } + code.Preorder(pass, fn, (*ast.BinaryExpr)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/simple/s1003/s1003.go b/tools/vendor/honnef.co/go/tools/simple/s1003/s1003.go new file mode 100644 index 0000000000..aa649c5d52 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/simple/s1003/s1003.go @@ -0,0 +1,118 @@ +package s1003 + +import ( + "fmt" + "go/ast" + "go/token" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/edit" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "S1003", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Replace call to \'strings.Index\' with \'strings.Contains\'`, + Before: `if strings.Index(x, y) != -1 {}`, + After: `if strings.Contains(x, y) {}`, + Since: "2017.1", + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + // map of value to token to bool value + allowed := map[int64]map[token.Token]bool{ + -1: {token.GTR: true, token.NEQ: true, token.EQL: false}, + 0: {token.GEQ: true, token.LSS: false}, + } + fn := func(node ast.Node) { + expr := node.(*ast.BinaryExpr) + switch expr.Op { + case token.GEQ, token.GTR, token.NEQ, token.LSS, token.EQL: + default: + return + } + + value, ok := code.ExprToInt(pass, expr.Y) + if !ok { + return + } + + allowedOps, ok := allowed[value] + if !ok { + return + } + b, ok := allowedOps[expr.Op] + if !ok { + return + } + + call, ok := expr.X.(*ast.CallExpr) + if !ok { + return + } + sel, ok := call.Fun.(*ast.SelectorExpr) + if !ok { + return + } + pkgIdent, ok := sel.X.(*ast.Ident) + if !ok { + return + } + funIdent := sel.Sel + if pkgIdent.Name != "strings" && pkgIdent.Name != "bytes" { + return + } + + var r ast.Expr + switch funIdent.Name { + case "IndexRune": + r = &ast.SelectorExpr{ + X: pkgIdent, + Sel: &ast.Ident{Name: "ContainsRune"}, + } + case "IndexAny": + r = &ast.SelectorExpr{ + X: pkgIdent, + Sel: &ast.Ident{Name: "ContainsAny"}, + } + case "Index": + r = &ast.SelectorExpr{ + X: pkgIdent, + Sel: &ast.Ident{Name: "Contains"}, + } + default: + return + } + + r = &ast.CallExpr{ + Fun: r, + Args: call.Args, + } + if !b { + r = &ast.UnaryExpr{ + Op: token.NOT, + X: r, + } + } + + report.Report(pass, node, fmt.Sprintf("should use %s instead", report.Render(pass, r)), + report.FilterGenerated(), + report.Fixes(edit.Fix(fmt.Sprintf("simplify use of %s", report.Render(pass, call.Fun)), edit.ReplaceWithNode(pass.Fset, node, r)))) + } + code.Preorder(pass, fn, (*ast.BinaryExpr)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/simple/s1004/s1004.go b/tools/vendor/honnef.co/go/tools/simple/s1004/s1004.go new file mode 100644 index 0000000000..d2b7f58d07 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/simple/s1004/s1004.go @@ -0,0 +1,72 @@ +package s1004 + +import ( + "fmt" + "go/ast" + "go/token" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/edit" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "S1004", + Run: CheckBytesCompare, + Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Replace call to \'bytes.Compare\' with \'bytes.Equal\'`, + Before: `if bytes.Compare(x, y) == 0 {}`, + After: `if bytes.Equal(x, y) {}`, + Since: "2017.1", + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var ( + checkBytesCompareQ = pattern.MustParse(`(BinaryExpr (CallExpr (Symbol "bytes.Compare") args) op@(Or "==" "!=") (IntegerLiteral "0"))`) + checkBytesCompareRe = pattern.MustParse(`(CallExpr (SelectorExpr (Ident "bytes") (Ident "Equal")) args)`) + checkBytesCompareRn = pattern.MustParse(`(UnaryExpr "!" (CallExpr (SelectorExpr (Ident "bytes") (Ident "Equal")) args))`) +) + +func CheckBytesCompare(pass *analysis.Pass) (interface{}, error) { + if pass.Pkg.Path() == "bytes" || pass.Pkg.Path() == "bytes_test" { + // the bytes package is free to use bytes.Compare as it sees fit + return nil, nil + } + fn := func(node ast.Node) { + m, ok := code.Match(pass, checkBytesCompareQ, node) + if !ok { + return + } + + args := report.RenderArgs(pass, m.State["args"].([]ast.Expr)) + prefix := "" + if m.State["op"].(token.Token) == token.NEQ { + prefix = "!" + } + + var fix analysis.SuggestedFix + switch tok := m.State["op"].(token.Token); tok { + case token.EQL: + fix = edit.Fix("simplify use of bytes.Compare", edit.ReplaceWithPattern(pass.Fset, node, checkBytesCompareRe, m.State)) + case token.NEQ: + fix = edit.Fix("simplify use of bytes.Compare", edit.ReplaceWithPattern(pass.Fset, node, checkBytesCompareRn, m.State)) + default: + panic(fmt.Sprintf("unexpected token %v", tok)) + } + report.Report(pass, node, fmt.Sprintf("should use %sbytes.Equal(%s) instead", prefix, args), report.FilterGenerated(), report.Fixes(fix)) + } + code.Preorder(pass, fn, (*ast.BinaryExpr)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/simple/s1005/s1005.go b/tools/vendor/honnef.co/go/tools/simple/s1005/s1005.go new file mode 100644 index 0000000000..e84ede29a1 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/simple/s1005/s1005.go @@ -0,0 +1,108 @@ +package s1005 + +import ( + "go/ast" + "go/types" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/edit" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/ast/astutil" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "S1005", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Drop unnecessary use of the blank identifier`, + Text: `In many cases, assigning to the blank identifier is unnecessary.`, + Before: ` +for _ = range s {} +x, _ = someMap[key] +_ = <-ch`, + After: ` +for range s{} +x = someMap[key] +<-ch`, + Since: "2017.1", + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var ( + checkUnnecessaryBlankQ1 = pattern.MustParse(` + (AssignStmt + [_ (Ident "_")] + _ + (Or + (IndexExpr _ _) + (UnaryExpr "<-" _))) `) + checkUnnecessaryBlankQ2 = pattern.MustParse(` + (AssignStmt + (Ident "_") _ recv@(UnaryExpr "<-" _))`) +) + +func run(pass *analysis.Pass) (interface{}, error) { + fn1 := func(node ast.Node) { + if _, ok := code.Match(pass, checkUnnecessaryBlankQ1, node); ok { + r := *node.(*ast.AssignStmt) + r.Lhs = r.Lhs[0:1] + report.Report(pass, node, "unnecessary assignment to the blank identifier", + report.FilterGenerated(), + report.Fixes(edit.Fix("remove assignment to blank identifier", edit.ReplaceWithNode(pass.Fset, node, &r)))) + } else if m, ok := code.Match(pass, checkUnnecessaryBlankQ2, node); ok { + report.Report(pass, node, "unnecessary assignment to the blank identifier", + report.FilterGenerated(), + report.Fixes(edit.Fix("simplify channel receive operation", edit.ReplaceWithNode(pass.Fset, node, m.State["recv"].(ast.Node))))) + } + } + + fn3 := func(node ast.Node) { + rs := node.(*ast.RangeStmt) + + if _, ok := pass.TypesInfo.TypeOf(rs.X).Underlying().(*types.Signature); ok { + // iteration variables are not optional with rangefunc + return + } + + // for _ + if rs.Value == nil && astutil.IsBlank(rs.Key) { + report.Report(pass, rs.Key, "unnecessary assignment to the blank identifier", + report.FilterGenerated(), + report.MinimumLanguageVersion("go1.4"), + report.Fixes(edit.Fix("remove assignment to blank identifier", edit.Delete(edit.Range{rs.Key.Pos(), rs.TokPos + 1})))) + } + + // for _, _ + if astutil.IsBlank(rs.Key) && astutil.IsBlank(rs.Value) { + // FIXME we should mark both key and value + report.Report(pass, rs.Key, "unnecessary assignment to the blank identifier", + report.FilterGenerated(), + report.MinimumLanguageVersion("go1.4"), + report.Fixes(edit.Fix("remove assignment to blank identifier", edit.Delete(edit.Range{rs.Key.Pos(), rs.TokPos + 1})))) + } + + // for x, _ + if !astutil.IsBlank(rs.Key) && astutil.IsBlank(rs.Value) { + report.Report(pass, rs.Value, "unnecessary assignment to the blank identifier", + report.FilterGenerated(), + report.MinimumLanguageVersion("go1.4"), + report.Fixes(edit.Fix("remove assignment to blank identifier", edit.Delete(edit.Range{rs.Key.End(), rs.Value.End()})))) + } + } + + code.Preorder(pass, fn1, (*ast.AssignStmt)(nil)) + code.Preorder(pass, fn3, (*ast.RangeStmt)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/simple/s1006/s1006.go b/tools/vendor/honnef.co/go/tools/simple/s1006/s1006.go new file mode 100644 index 0000000000..fa177531e9 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/simple/s1006/s1006.go @@ -0,0 +1,46 @@ +package s1006 + +import ( + "go/ast" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "S1006", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Use \"for { ... }\" for infinite loops`, + Text: `For infinite loops, using \'for { ... }\' is the most idiomatic choice.`, + Since: "2017.1", + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + loop := node.(*ast.ForStmt) + if loop.Init != nil || loop.Post != nil { + return + } + if !code.IsBoolConst(pass, loop.Cond) || !code.BoolConst(pass, loop.Cond) { + return + } + report.Report(pass, loop, "should use for {} instead of for true {}", + report.ShortRange(), + report.FilterGenerated()) + } + code.Preorder(pass, fn, (*ast.ForStmt)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/simple/s1007/s1007.go b/tools/vendor/honnef.co/go/tools/simple/s1007/s1007.go new file mode 100644 index 0000000000..ac42c8d18e --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/simple/s1007/s1007.go @@ -0,0 +1,93 @@ +package s1007 + +import ( + "fmt" + "go/ast" + "go/token" + "strings" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/knowledge" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "S1007", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Simplify regular expression by using raw string literal`, + Text: `Raw string literals use backticks instead of quotation marks and do not support +any escape sequences. This means that the backslash can be used +freely, without the need of escaping. + +Since regular expressions have their own escape sequences, raw strings +can improve their readability.`, + Before: `regexp.Compile("\\A(\\w+) profile: total \\d+\\n\\z")`, + After: "regexp.Compile(`\\A(\\w+) profile: total \\d+\\n\\z`)", + Since: "2017.1", + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + call := node.(*ast.CallExpr) + if !code.IsCallToAny(pass, call, "regexp.MustCompile", "regexp.Compile") { + return + } + sel, ok := call.Fun.(*ast.SelectorExpr) + if !ok { + return + } + lit, ok := call.Args[knowledge.Arg("regexp.Compile.expr")].(*ast.BasicLit) + if !ok { + // TODO(dominikh): support string concat, maybe support constants + return + } + if lit.Kind != token.STRING { + // invalid function call + return + } + if lit.Value[0] != '"' { + // already a raw string + return + } + val := lit.Value + if !strings.Contains(val, `\\`) { + return + } + if strings.Contains(val, "`") { + return + } + + bs := false + for _, c := range val { + if !bs && c == '\\' { + bs = true + continue + } + if bs && c == '\\' { + bs = false + continue + } + if bs { + // backslash followed by non-backslash -> escape sequence + return + } + } + + report.Report(pass, call, fmt.Sprintf("should use raw string (`...`) with regexp.%s to avoid having to escape twice", sel.Sel.Name), report.FilterGenerated()) + } + code.Preorder(pass, fn, (*ast.CallExpr)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/simple/s1008/s1008.go b/tools/vendor/honnef.co/go/tools/simple/s1008/s1008.go new file mode 100644 index 0000000000..98cd00dac7 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/simple/s1008/s1008.go @@ -0,0 +1,148 @@ +package s1008 + +import ( + "fmt" + "go/ast" + "go/constant" + "go/token" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "S1008", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Simplify returning boolean expression`, + Before: ` +if { + return true +} +return false`, + After: `return `, + Since: "2017.1", + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var ( + checkIfReturnQIf = pattern.MustParse(`(IfStmt nil cond [(ReturnStmt [ret@(Builtin (Or "true" "false"))])] nil)`) + checkIfReturnQRet = pattern.MustParse(`(ReturnStmt [ret@(Builtin (Or "true" "false"))])`) +) + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + block := node.(*ast.BlockStmt) + l := len(block.List) + if l < 2 { + return + } + n1, n2 := block.List[l-2], block.List[l-1] + + if len(block.List) >= 3 { + if _, ok := block.List[l-3].(*ast.IfStmt); ok { + // Do not flag a series of if statements + return + } + } + m1, ok := code.Match(pass, checkIfReturnQIf, n1) + if !ok { + return + } + m2, ok := code.Match(pass, checkIfReturnQRet, n2) + if !ok { + return + } + + if op, ok := m1.State["cond"].(*ast.BinaryExpr); ok { + switch op.Op { + case token.EQL, token.LSS, token.GTR, token.NEQ, token.LEQ, token.GEQ: + default: + return + } + } + + ret1 := m1.State["ret"].(*ast.Ident) + ret2 := m2.State["ret"].(*ast.Ident) + + if ret1.Name == ret2.Name { + // we want the function to return true and false, not the + // same value both times. + return + } + + cond := m1.State["cond"].(ast.Expr) + origCond := cond + if ret1.Name == "false" { + cond = negate(pass, cond) + } + report.Report(pass, n1, + fmt.Sprintf("should use 'return %s' instead of 'if %s { return %s }; return %s'", + report.Render(pass, cond), + report.Render(pass, origCond), report.Render(pass, ret1), report.Render(pass, ret2)), + report.FilterGenerated()) + } + code.Preorder(pass, fn, (*ast.BlockStmt)(nil)) + return nil, nil +} + +func negate(pass *analysis.Pass, expr ast.Expr) ast.Expr { + switch expr := expr.(type) { + case *ast.BinaryExpr: + out := *expr + switch expr.Op { + case token.EQL: + out.Op = token.NEQ + case token.LSS: + out.Op = token.GEQ + case token.GTR: + // Some builtins never return negative ints; "len(x) <= 0" should be "len(x) == 0". + if call, ok := expr.X.(*ast.CallExpr); ok && + code.IsCallToAny(pass, call, "len", "cap", "copy") && + code.IsIntegerLiteral(pass, expr.Y, constant.MakeInt64(0)) { + out.Op = token.EQL + } else { + out.Op = token.LEQ + } + case token.NEQ: + out.Op = token.EQL + case token.LEQ: + out.Op = token.GTR + case token.GEQ: + out.Op = token.LSS + } + return &out + case *ast.Ident, *ast.CallExpr, *ast.IndexExpr, *ast.StarExpr: + return &ast.UnaryExpr{ + Op: token.NOT, + X: expr, + } + case *ast.UnaryExpr: + if expr.Op == token.NOT { + return expr.X + } + return &ast.UnaryExpr{ + Op: token.NOT, + X: expr, + } + default: + return &ast.UnaryExpr{ + Op: token.NOT, + X: &ast.ParenExpr{ + X: expr, + }, + } + } +} diff --git a/tools/vendor/honnef.co/go/tools/simple/s1009/s1009.go b/tools/vendor/honnef.co/go/tools/simple/s1009/s1009.go new file mode 100644 index 0000000000..afe5c3c149 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/simple/s1009/s1009.go @@ -0,0 +1,185 @@ +package s1009 + +import ( + "fmt" + "go/ast" + "go/constant" + "go/token" + "go/types" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/types/typeutil" + "honnef.co/go/tools/knowledge" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "S1009", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Omit redundant nil check on slices, maps, and channels`, + Text: `The \'len\' function is defined for all slices, maps, and +channels, even nil ones, which have a length of zero. It is not necessary to +check for nil before checking that their length is not zero.`, + Before: `if x != nil && len(x) != 0 {}`, + After: `if len(x) != 0 {}`, + Since: "2017.1", + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +// run checks for the following redundant nil-checks: +// +// if x == nil || len(x) == 0 {} +// if x != nil && len(x) != 0 {} +// if x != nil && len(x) == N {} (where N != 0) +// if x != nil && len(x) > N {} +// if x != nil && len(x) >= N {} (where N != 0) +func run(pass *analysis.Pass) (interface{}, error) { + isConstZero := func(expr ast.Expr) (isConst bool, isZero bool) { + _, ok := expr.(*ast.BasicLit) + if ok { + return true, code.IsIntegerLiteral(pass, expr, constant.MakeInt64(0)) + } + id, ok := expr.(*ast.Ident) + if !ok { + return false, false + } + c, ok := pass.TypesInfo.ObjectOf(id).(*types.Const) + if !ok { + return false, false + } + return true, c.Val().Kind() == constant.Int && c.Val().String() == "0" + } + + fn := func(node ast.Node) { + // check that expr is "x || y" or "x && y" + expr := node.(*ast.BinaryExpr) + if expr.Op != token.LOR && expr.Op != token.LAND { + return + } + eqNil := expr.Op == token.LOR + + // check that x is "xx == nil" or "xx != nil" + x, ok := expr.X.(*ast.BinaryExpr) + if !ok { + return + } + if eqNil && x.Op != token.EQL { + return + } + if !eqNil && x.Op != token.NEQ { + return + } + var xx *ast.Ident + switch s := x.X.(type) { + case *ast.Ident: + xx = s + case *ast.SelectorExpr: + xx = s.Sel + default: + return + } + if !code.IsNil(pass, x.Y) { + return + } + + // check that y is "len(xx) == 0" or "len(xx) ... " + y, ok := expr.Y.(*ast.BinaryExpr) + if !ok { + return + } + if eqNil && y.Op != token.EQL { // must be len(xx) *==* 0 + return + } + yx, ok := y.X.(*ast.CallExpr) + if !ok { + return + } + if !code.IsCallTo(pass, yx, "len") { + return + } + var yxArg *ast.Ident + switch s := yx.Args[knowledge.Arg("len.v")].(type) { + case *ast.Ident: + yxArg = s + case *ast.SelectorExpr: + yxArg = s.Sel + default: + return + } + if yxArg.Name != xx.Name { + return + } + + if eqNil && !code.IsIntegerLiteral(pass, y.Y, constant.MakeInt64(0)) { // must be len(x) == *0* + return + } + + if !eqNil { + isConst, isZero := isConstZero(y.Y) + if !isConst { + return + } + switch y.Op { + case token.EQL: + // avoid false positive for "xx != nil && len(xx) == 0" + if isZero { + return + } + case token.GEQ: + // avoid false positive for "xx != nil && len(xx) >= 0" + if isZero { + return + } + case token.NEQ: + // avoid false positive for "xx != nil && len(xx) != " + if !isZero { + return + } + case token.GTR: + // ok + default: + return + } + } + + // finally check that xx type is one of array, slice, map or chan + // this is to prevent false positive in case if xx is a pointer to an array + typ := pass.TypesInfo.TypeOf(xx) + ok = typeutil.All(typ, func(term *types.Term) bool { + switch term.Type().Underlying().(type) { + case *types.Slice: + return true + case *types.Map: + return true + case *types.Chan: + return true + case *types.Pointer: + return false + case *types.TypeParam: + return false + default: + lint.ExhaustiveTypeSwitch(term.Type().Underlying()) + return false + } + }) + if !ok { + return + } + + report.Report(pass, expr, fmt.Sprintf("should omit nil check; len() for %s is defined as zero", typ), report.FilterGenerated()) + } + code.Preorder(pass, fn, (*ast.BinaryExpr)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/simple/s1010/s1010.go b/tools/vendor/honnef.co/go/tools/simple/s1010/s1010.go new file mode 100644 index 0000000000..0545718a42 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/simple/s1010/s1010.go @@ -0,0 +1,48 @@ +package s1010 + +import ( + "go/ast" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/edit" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "S1010", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Omit default slice index`, + Text: `When slicing, the second index defaults to the length of the value, +making \'s[n:len(s)]\' and \'s[n:]\' equivalent.`, + Since: "2017.1", + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var checkSlicingQ = pattern.MustParse(`(SliceExpr x@(Object _) low (CallExpr (Builtin "len") [x]) nil)`) + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + if _, ok := code.Match(pass, checkSlicingQ, node); ok { + expr := node.(*ast.SliceExpr) + report.Report(pass, expr.High, + "should omit second index in slice, s[a:len(s)] is identical to s[a:]", + report.FilterGenerated(), + report.Fixes(edit.Fix("simplify slice expression", edit.Delete(expr.High)))) + } + } + code.Preorder(pass, fn, (*ast.SliceExpr)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/simple/s1011/s1011.go b/tools/vendor/honnef.co/go/tools/simple/s1011/s1011.go new file mode 100644 index 0000000000..73169266d4 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/simple/s1011/s1011.go @@ -0,0 +1,144 @@ +package s1011 + +import ( + "fmt" + "go/ast" + "go/token" + "go/types" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/edit" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/facts/purity" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "S1011", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer, purity.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Use a single \'append\' to concatenate two slices`, + Before: ` +for _, e := range y { + x = append(x, e) +} + +for i := range y { + x = append(x, y[i]) +} + +for i := range y { + v := y[i] + x = append(x, v) +}`, + + After: ` +x = append(x, y...) +x = append(x, y...) +x = append(x, y...)`, + Since: "2017.1", + // MergeIfAll because y might not be a slice under all build tags. + MergeIf: lint.MergeIfAll, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var checkLoopAppendQ = pattern.MustParse(` +(Or + (RangeStmt + (Ident "_") + val@(Object _) + _ + x + [(AssignStmt [lhs] "=" [(CallExpr (Builtin "append") [lhs val])])]) + (RangeStmt + idx@(Object _) + nil + _ + x + [(AssignStmt [lhs] "=" [(CallExpr (Builtin "append") [lhs (IndexExpr x idx)])])]) + (RangeStmt + idx@(Object _) + nil + _ + x + [(AssignStmt val@(Object _) ":=" (IndexExpr x idx)) + (AssignStmt [lhs] "=" [(CallExpr (Builtin "append") [lhs val])])]))`) + +func run(pass *analysis.Pass) (interface{}, error) { + pure := pass.ResultOf[purity.Analyzer].(purity.Result) + + fn := func(node ast.Node) { + m, ok := code.Match(pass, checkLoopAppendQ, node) + if !ok { + return + } + + if val, ok := m.State["val"].(types.Object); ok && code.RefersTo(pass, m.State["lhs"].(ast.Expr), val) { + return + } + + if m.State["idx"] != nil && code.MayHaveSideEffects(pass, m.State["x"].(ast.Expr), pure) { + // When using an index-based loop, x gets evaluated repeatedly and thus should be pure. + // This doesn't matter for value-based loops, because x only gets evaluated once. + return + } + + if idx, ok := m.State["idx"].(types.Object); ok && code.RefersTo(pass, m.State["lhs"].(ast.Expr), idx) { + // The lhs mustn't refer to the index loop variable. + return + } + + if code.MayHaveSideEffects(pass, m.State["lhs"].(ast.Expr), pure) { + // The lhs may be dynamic and return different values on each iteration. For example: + // + // func bar() map[int][]int { /* return one of several maps */ } + // + // func foo(x []int, y [][]int) { + // for i := range x { + // bar()[0] = append(bar()[0], x[i]) + // } + // } + // + // The dynamic nature of the lhs might also affect the value of the index. + return + } + + src := pass.TypesInfo.TypeOf(m.State["x"].(ast.Expr)) + dst := pass.TypesInfo.TypeOf(m.State["lhs"].(ast.Expr)) + if !types.Identical(src, dst) { + return + } + + r := &ast.AssignStmt{ + Lhs: []ast.Expr{m.State["lhs"].(ast.Expr)}, + Tok: token.ASSIGN, + Rhs: []ast.Expr{ + &ast.CallExpr{ + Fun: &ast.Ident{Name: "append"}, + Args: []ast.Expr{ + m.State["lhs"].(ast.Expr), + m.State["x"].(ast.Expr), + }, + Ellipsis: 1, + }, + }, + } + + report.Report(pass, node, fmt.Sprintf("should replace loop with %s", report.Render(pass, r)), + report.ShortRange(), + report.FilterGenerated(), + report.Fixes(edit.Fix("replace loop with call to append", edit.ReplaceWithNode(pass.Fset, node, r)))) + } + code.Preorder(pass, fn, (*ast.RangeStmt)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/simple/s1012/s1012.go b/tools/vendor/honnef.co/go/tools/simple/s1012/s1012.go new file mode 100644 index 0000000000..ceabd2cfff --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/simple/s1012/s1012.go @@ -0,0 +1,51 @@ +package s1012 + +import ( + "go/ast" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/edit" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "S1012", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Replace \'time.Now().Sub(x)\' with \'time.Since(x)\'`, + Text: `The \'time.Since\' helper has the same effect as using \'time.Now().Sub(x)\' +but is easier to read.`, + Before: `time.Now().Sub(x)`, + After: `time.Since(x)`, + Since: "2017.1", + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var ( + checkTimeSinceQ = pattern.MustParse(`(CallExpr (SelectorExpr (CallExpr (Symbol "time.Now") []) (Symbol "(time.Time).Sub")) [arg])`) + checkTimeSinceR = pattern.MustParse(`(CallExpr (SelectorExpr (Ident "time") (Ident "Since")) [arg])`) +) + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + if _, edits, ok := code.MatchAndEdit(pass, checkTimeSinceQ, checkTimeSinceR, node); ok { + report.Report(pass, node, "should use time.Since instead of time.Now().Sub", + report.FilterGenerated(), + report.Fixes(edit.Fix("replace with call to time.Since", edits...))) + } + } + code.Preorder(pass, fn, (*ast.CallExpr)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/simple/s1016/s1016.go b/tools/vendor/honnef.co/go/tools/simple/s1016/s1016.go new file mode 100644 index 0000000000..01c844aa7b --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/simple/s1016/s1016.go @@ -0,0 +1,189 @@ +package s1016 + +import ( + "fmt" + "go/ast" + "go/token" + "go/types" + "go/version" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/edit" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "S1016", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Use a type conversion instead of manually copying struct fields`, + Text: `Two struct types with identical fields can be converted between each +other. In older versions of Go, the fields had to have identical +struct tags. Since Go 1.8, however, struct tags are ignored during +conversions. It is thus not necessary to manually copy every field +individually.`, + Before: ` +var x T1 +y := T2{ + Field1: x.Field1, + Field2: x.Field2, +}`, + After: ` +var x T1 +y := T2(x)`, + Since: "2017.1", + MergeIf: lint.MergeIfAll, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + // TODO(dh): support conversions between type parameters + fn := func(node ast.Node, stack []ast.Node) { + if unary, ok := stack[len(stack)-2].(*ast.UnaryExpr); ok && unary.Op == token.AND { + // Do not suggest type conversion between pointers + return + } + + lit := node.(*ast.CompositeLit) + var typ1 types.Type + var named1 *types.Named + switch typ := pass.TypesInfo.TypeOf(lit.Type).(type) { + case *types.Named: + typ1 = typ + named1 = typ + case *types.Alias: + ua := types.Unalias(typ) + if n, ok := ua.(*types.Named); ok { + typ1 = typ + named1 = n + } + } + if typ1 == nil { + return + } + s1, ok := typ1.Underlying().(*types.Struct) + if !ok { + return + } + + var typ2 types.Type + var named2 *types.Named + var ident *ast.Ident + getSelType := func(expr ast.Expr) (types.Type, *ast.Ident, bool) { + sel, ok := expr.(*ast.SelectorExpr) + if !ok { + return nil, nil, false + } + ident, ok := sel.X.(*ast.Ident) + if !ok { + return nil, nil, false + } + typ := pass.TypesInfo.TypeOf(sel.X) + return typ, ident, typ != nil + } + if len(lit.Elts) == 0 { + return + } + if s1.NumFields() != len(lit.Elts) { + return + } + for i, elt := range lit.Elts { + var t types.Type + var id *ast.Ident + var ok bool + switch elt := elt.(type) { + case *ast.SelectorExpr: + t, id, ok = getSelType(elt) + if !ok { + return + } + if i >= s1.NumFields() || s1.Field(i).Name() != elt.Sel.Name { + return + } + case *ast.KeyValueExpr: + var sel *ast.SelectorExpr + sel, ok = elt.Value.(*ast.SelectorExpr) + if !ok { + return + } + + if elt.Key.(*ast.Ident).Name != sel.Sel.Name { + return + } + t, id, ok = getSelType(elt.Value) + } + if !ok { + return + } + // All fields must be initialized from the same object + if ident != nil && pass.TypesInfo.ObjectOf(ident) != pass.TypesInfo.ObjectOf(id) { + return + } + switch t := t.(type) { + case *types.Named: + typ2 = t + named2 = t + case *types.Alias: + if n, ok := types.Unalias(t).(*types.Named); ok { + typ2 = t + named2 = n + } + } + if typ2 == nil { + return + } + ident = id + } + + if typ2 == nil { + return + } + + if named1.Obj().Pkg() != named2.Obj().Pkg() { + // Do not suggest type conversions between different + // packages. Types in different packages might only match + // by coincidence. Furthermore, if the dependency ever + // adds more fields to its type, it could break the code + // that relies on the type conversion to work. + return + } + + s2, ok := typ2.Underlying().(*types.Struct) + if !ok { + return + } + if typ1 == typ2 { + return + } + if version.Compare(code.LanguageVersion(pass, node), "go1.8") >= 0 { + if !types.IdenticalIgnoreTags(s1, s2) { + return + } + } else { + if !types.Identical(s1, s2) { + return + } + } + + r := &ast.CallExpr{ + Fun: lit.Type, + Args: []ast.Expr{ident}, + } + report.Report(pass, node, + fmt.Sprintf("should convert %s (type %s) to %s instead of using struct literal", ident.Name, types.TypeString(typ2, types.RelativeTo(pass.Pkg)), types.TypeString(typ1, types.RelativeTo(pass.Pkg))), + report.FilterGenerated(), + report.Fixes(edit.Fix("use type conversion", edit.ReplaceWithNode(pass.Fset, node, r)))) + } + code.PreorderStack(pass, fn, (*ast.CompositeLit)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/simple/s1017/s1017.go b/tools/vendor/honnef.co/go/tools/simple/s1017/s1017.go new file mode 100644 index 0000000000..aa99b7c2a6 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/simple/s1017/s1017.go @@ -0,0 +1,239 @@ +package s1017 + +import ( + "fmt" + "go/ast" + "go/token" + "reflect" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/ast/astutil" + "honnef.co/go/tools/knowledge" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "S1017", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Replace manual trimming with \'strings.TrimPrefix\'`, + Text: `Instead of using \'strings.HasPrefix\' and manual slicing, use the +\'strings.TrimPrefix\' function. If the string doesn't start with the +prefix, the original string will be returned. Using \'strings.TrimPrefix\' +reduces complexity, and avoids common bugs, such as off-by-one +mistakes.`, + Before: ` +if strings.HasPrefix(str, prefix) { + str = str[len(prefix):] +}`, + After: `str = strings.TrimPrefix(str, prefix)`, + Since: "2017.1", + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + sameNonDynamic := func(node1, node2 ast.Node) bool { + if reflect.TypeOf(node1) != reflect.TypeOf(node2) { + return false + } + + switch node1 := node1.(type) { + case *ast.Ident: + return pass.TypesInfo.ObjectOf(node1) == pass.TypesInfo.ObjectOf(node2.(*ast.Ident)) + case *ast.SelectorExpr, *ast.IndexExpr: + return astutil.Equal(node1, node2) + case *ast.BasicLit: + return astutil.Equal(node1, node2) + } + return false + } + + isLenOnIdent := func(fn ast.Expr, ident ast.Expr) bool { + call, ok := fn.(*ast.CallExpr) + if !ok { + return false + } + if !code.IsCallTo(pass, call, "len") { + return false + } + if len(call.Args) != 1 { + return false + } + return sameNonDynamic(call.Args[knowledge.Arg("len.v")], ident) + } + + seen := make(map[ast.Node]struct{}) + fn := func(node ast.Node) { + var pkg string + var fun string + + ifstmt := node.(*ast.IfStmt) + if ifstmt.Init != nil { + return + } + if ifstmt.Else != nil { + seen[ifstmt.Else] = struct{}{} + return + } + if _, ok := seen[ifstmt]; ok { + return + } + if len(ifstmt.Body.List) != 1 { + return + } + condCall, ok := ifstmt.Cond.(*ast.CallExpr) + if !ok { + return + } + + condCallName := code.CallName(pass, condCall) + switch condCallName { + case "strings.HasPrefix": + pkg = "strings" + fun = "HasPrefix" + case "strings.HasSuffix": + pkg = "strings" + fun = "HasSuffix" + case "strings.Contains": + pkg = "strings" + fun = "Contains" + case "bytes.HasPrefix": + pkg = "bytes" + fun = "HasPrefix" + case "bytes.HasSuffix": + pkg = "bytes" + fun = "HasSuffix" + case "bytes.Contains": + pkg = "bytes" + fun = "Contains" + default: + return + } + + assign, ok := ifstmt.Body.List[0].(*ast.AssignStmt) + if !ok { + return + } + if assign.Tok != token.ASSIGN { + return + } + if len(assign.Lhs) != 1 || len(assign.Rhs) != 1 { + return + } + if !sameNonDynamic(condCall.Args[0], assign.Lhs[0]) { + return + } + + switch rhs := assign.Rhs[0].(type) { + case *ast.CallExpr: + if len(rhs.Args) < 2 || !sameNonDynamic(condCall.Args[0], rhs.Args[0]) || !sameNonDynamic(condCall.Args[1], rhs.Args[1]) { + return + } + + rhsName := code.CallName(pass, rhs) + if condCallName == "strings.HasPrefix" && rhsName == "strings.TrimPrefix" || + condCallName == "strings.HasSuffix" && rhsName == "strings.TrimSuffix" || + condCallName == "strings.Contains" && rhsName == "strings.Replace" || + condCallName == "bytes.HasPrefix" && rhsName == "bytes.TrimPrefix" || + condCallName == "bytes.HasSuffix" && rhsName == "bytes.TrimSuffix" || + condCallName == "bytes.Contains" && rhsName == "bytes.Replace" { + report.Report(pass, ifstmt, fmt.Sprintf("should replace this if statement with an unconditional %s", rhsName), report.FilterGenerated()) + } + case *ast.SliceExpr: + slice := rhs + if !ok { + return + } + if slice.Slice3 { + return + } + if !sameNonDynamic(slice.X, condCall.Args[0]) { + return + } + + validateOffset := func(off ast.Expr) bool { + switch off := off.(type) { + case *ast.CallExpr: + return isLenOnIdent(off, condCall.Args[1]) + case *ast.BasicLit: + if pkg != "strings" { + return false + } + if _, ok := condCall.Args[1].(*ast.BasicLit); !ok { + // Only allow manual slicing with an integer + // literal if the second argument to HasPrefix + // was a string literal. + return false + } + s, ok1 := code.ExprToString(pass, condCall.Args[1]) + n, ok2 := code.ExprToInt(pass, off) + if !ok1 || !ok2 || n != int64(len(s)) { + return false + } + return true + default: + return false + } + } + + switch fun { + case "HasPrefix": + // TODO(dh) We could detect a High that is len(s), but another + // rule will already flag that, anyway. + if slice.High != nil { + return + } + if !validateOffset(slice.Low) { + return + } + case "HasSuffix": + if slice.Low != nil { + n, ok := code.ExprToInt(pass, slice.Low) + if !ok || n != 0 { + return + } + } + switch index := slice.High.(type) { + case *ast.BinaryExpr: + if index.Op != token.SUB { + return + } + if !isLenOnIdent(index.X, condCall.Args[0]) { + return + } + if !validateOffset(index.Y) { + return + } + default: + return + } + default: + return + } + + var replacement string + switch fun { + case "HasPrefix": + replacement = "TrimPrefix" + case "HasSuffix": + replacement = "TrimSuffix" + } + report.Report(pass, ifstmt, fmt.Sprintf("should replace this if statement with an unconditional %s.%s", pkg, replacement), + report.ShortRange(), + report.FilterGenerated()) + } + } + code.Preorder(pass, fn, (*ast.IfStmt)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/simple/s1018/s1018.go b/tools/vendor/honnef.co/go/tools/simple/s1018/s1018.go new file mode 100644 index 0000000000..ef3ee897cd --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/simple/s1018/s1018.go @@ -0,0 +1,84 @@ +package s1018 + +import ( + "go/ast" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/edit" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/types/typeutil" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "S1018", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Use \"copy\" for sliding elements`, + Text: `\'copy()\' permits using the same source and destination slice, even with +overlapping ranges. This makes it ideal for sliding elements in a +slice.`, + + Before: ` +for i := 0; i < n; i++ { + bs[i] = bs[offset+i] +}`, + After: `copy(bs[:n], bs[offset:])`, + Since: "2017.1", + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var ( + checkLoopSlideQ = pattern.MustParse(` + (ForStmt + (AssignStmt initvar@(Ident _) _ (IntegerLiteral "0")) + (BinaryExpr initvar "<" limit@(Ident _)) + (IncDecStmt initvar "++") + [(AssignStmt + (IndexExpr slice@(Ident _) initvar) + "=" + (IndexExpr slice (BinaryExpr offset@(Ident _) "+" initvar)))])`) + checkLoopSlideR = pattern.MustParse(` + (CallExpr + (Ident "copy") + [(SliceExpr slice nil limit nil) + (SliceExpr slice offset nil nil)])`) +) + +func run(pass *analysis.Pass) (interface{}, error) { + // TODO(dh): detect bs[i+offset] in addition to bs[offset+i] + // TODO(dh): consider merging this function with LintLoopCopy + // TODO(dh): detect length that is an expression, not a variable name + // TODO(dh): support sliding to a different offset than the beginning of the slice + + fn := func(node ast.Node) { + loop := node.(*ast.ForStmt) + m, edits, ok := code.MatchAndEdit(pass, checkLoopSlideQ, checkLoopSlideR, loop) + if !ok { + return + } + typ := pass.TypesInfo.TypeOf(m.State["slice"].(*ast.Ident)) + // The pattern probably needs a core type, but All is fine, too. Either way we only accept slices. + if !typeutil.All(typ, typeutil.IsSlice) { + return + } + + report.Report(pass, loop, "should use copy() instead of loop for sliding slice elements", + report.ShortRange(), + report.FilterGenerated(), + report.Fixes(edit.Fix("use copy() instead of loop", edits...))) + } + code.Preorder(pass, fn, (*ast.ForStmt)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/simple/s1019/s1019.go b/tools/vendor/honnef.co/go/tools/simple/s1019/s1019.go new file mode 100644 index 0000000000..4f3288edbd --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/simple/s1019/s1019.go @@ -0,0 +1,71 @@ +package s1019 + +import ( + "fmt" + "go/ast" + "go/types" + "path/filepath" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/types/typeutil" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "S1019", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Simplify \"make\" call by omitting redundant arguments`, + Text: `The \"make\" function has default values for the length and capacity +arguments. For channels, the length defaults to zero, and for slices, +the capacity defaults to the length.`, + Since: "2017.1", + // MergeIfAll because the type might be different under different build tags. + // You shouldn't write code like that… + MergeIf: lint.MergeIfAll, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var ( + checkMakeLenCapQ1 = pattern.MustParse(`(CallExpr (Builtin "make") [typ size@(IntegerLiteral "0")])`) + checkMakeLenCapQ2 = pattern.MustParse(`(CallExpr (Builtin "make") [typ size size])`) +) + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + if pass.Pkg.Path() == "runtime_test" && filepath.Base(pass.Fset.Position(node.Pos()).Filename) == "map_test.go" { + // special case of runtime tests testing map creation + return + } + if m, ok := code.Match(pass, checkMakeLenCapQ1, node); ok { + T := m.State["typ"].(ast.Expr) + size := m.State["size"].(ast.Node) + + if _, ok := typeutil.CoreType(pass.TypesInfo.TypeOf(T)).Underlying().(*types.Chan); ok { + report.Report(pass, size, fmt.Sprintf("should use make(%s) instead", report.Render(pass, T)), report.FilterGenerated()) + } + + } else if m, ok := code.Match(pass, checkMakeLenCapQ2, node); ok { + // TODO(dh): don't consider sizes identical if they're + // dynamic. for example: make(T, <-ch, <-ch). + T := m.State["typ"].(ast.Expr) + size := m.State["size"].(ast.Node) + report.Report(pass, size, + fmt.Sprintf("should use make(%s, %s) instead", report.Render(pass, T), report.Render(pass, size)), + report.FilterGenerated()) + } + } + code.Preorder(pass, fn, (*ast.CallExpr)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/simple/s1020/s1020.go b/tools/vendor/honnef.co/go/tools/simple/s1020/s1020.go new file mode 100644 index 0000000000..8795847efe --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/simple/s1020/s1020.go @@ -0,0 +1,86 @@ +package s1020 + +import ( + "fmt" + "go/ast" + "go/types" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "S1020", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Omit redundant nil check in type assertion`, + Before: `if _, ok := i.(T); ok && i != nil {}`, + After: `if _, ok := i.(T); ok {}`, + Since: "2017.1", + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var ( + checkAssertNotNilFn1Q = pattern.MustParse(` + (IfStmt + (AssignStmt [(Ident "_") ok@(Object _)] _ [(TypeAssertExpr assert@(Object _) _)]) + (Or + (BinaryExpr ok "&&" (BinaryExpr assert "!=" (Builtin "nil"))) + (BinaryExpr (BinaryExpr assert "!=" (Builtin "nil")) "&&" ok)) + _ + _)`) + checkAssertNotNilFn2Q = pattern.MustParse(` + (IfStmt + nil + (BinaryExpr lhs@(Object _) "!=" (Builtin "nil")) + [ + ifstmt@(IfStmt + (AssignStmt [(Ident "_") ok@(Object _)] _ [(TypeAssertExpr lhs _)]) + ok + _ + nil) + ] + nil)`) +) + +func run(pass *analysis.Pass) (interface{}, error) { + fn1 := func(node ast.Node) { + m, ok := code.Match(pass, checkAssertNotNilFn1Q, node) + if !ok { + return + } + assert := m.State["assert"].(types.Object) + assign := m.State["ok"].(types.Object) + report.Report(pass, node, fmt.Sprintf("when %s is true, %s can't be nil", assign.Name(), assert.Name()), + report.ShortRange(), + report.FilterGenerated()) + } + fn2 := func(node ast.Node) { + m, ok := code.Match(pass, checkAssertNotNilFn2Q, node) + if !ok { + return + } + ifstmt := m.State["ifstmt"].(*ast.IfStmt) + lhs := m.State["lhs"].(types.Object) + assignIdent := m.State["ok"].(types.Object) + report.Report(pass, ifstmt, fmt.Sprintf("when %s is true, %s can't be nil", assignIdent.Name(), lhs.Name()), + report.ShortRange(), + report.FilterGenerated()) + } + // OPT(dh): merge fn1 and fn2 + code.Preorder(pass, fn1, (*ast.IfStmt)(nil)) + code.Preorder(pass, fn2, (*ast.IfStmt)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/simple/s1021/s1021.go b/tools/vendor/honnef.co/go/tools/simple/s1021/s1021.go new file mode 100644 index 0000000000..6ea8e4f606 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/simple/s1021/s1021.go @@ -0,0 +1,118 @@ +package s1021 + +import ( + "go/ast" + "go/token" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/edit" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "S1021", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Merge variable declaration and assignment`, + Before: ` +var x uint +x = 1`, + After: `var x uint = 1`, + Since: "2017.1", + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + hasMultipleAssignments := func(root ast.Node, ident *ast.Ident) bool { + num := 0 + ast.Inspect(root, func(node ast.Node) bool { + if num >= 2 { + return false + } + assign, ok := node.(*ast.AssignStmt) + if !ok { + return true + } + for _, lhs := range assign.Lhs { + if oident, ok := lhs.(*ast.Ident); ok { + if pass.TypesInfo.ObjectOf(oident) == pass.TypesInfo.ObjectOf(ident) { + num++ + } + } + } + + return true + }) + return num >= 2 + } + fn := func(node ast.Node) { + block := node.(*ast.BlockStmt) + if len(block.List) < 2 { + return + } + for i, stmt := range block.List[:len(block.List)-1] { + _ = i + decl, ok := stmt.(*ast.DeclStmt) + if !ok { + continue + } + gdecl, ok := decl.Decl.(*ast.GenDecl) + if !ok || gdecl.Tok != token.VAR || len(gdecl.Specs) != 1 { + continue + } + vspec, ok := gdecl.Specs[0].(*ast.ValueSpec) + if !ok || len(vspec.Names) != 1 || len(vspec.Values) != 0 { + continue + } + + assign, ok := block.List[i+1].(*ast.AssignStmt) + if !ok || assign.Tok != token.ASSIGN { + continue + } + if len(assign.Lhs) != 1 || len(assign.Rhs) != 1 { + continue + } + ident, ok := assign.Lhs[0].(*ast.Ident) + if !ok { + continue + } + if pass.TypesInfo.ObjectOf(vspec.Names[0]) != pass.TypesInfo.ObjectOf(ident) { + continue + } + + if code.RefersTo(pass, assign.Rhs[0], pass.TypesInfo.ObjectOf(ident)) { + continue + } + if hasMultipleAssignments(block, ident) { + continue + } + + r := &ast.GenDecl{ + Specs: []ast.Spec{ + &ast.ValueSpec{ + Names: vspec.Names, + Values: []ast.Expr{assign.Rhs[0]}, + Type: vspec.Type, + }, + }, + Tok: gdecl.Tok, + } + report.Report(pass, decl, "should merge variable declaration with assignment on next line", + report.FilterGenerated(), + report.Fixes(edit.Fix("merge declaration with assignment", edit.ReplaceWithNode(pass.Fset, edit.Range{decl.Pos(), assign.End()}, r)))) + } + } + code.Preorder(pass, fn, (*ast.BlockStmt)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/simple/s1023/s1023.go b/tools/vendor/honnef.co/go/tools/simple/s1023/s1023.go new file mode 100644 index 0000000000..a2e8fcd2b0 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/simple/s1023/s1023.go @@ -0,0 +1,79 @@ +package s1023 + +import ( + "go/ast" + "go/token" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "S1023", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Omit redundant control flow`, + Text: `Functions that have no return value do not need a return statement as +the final statement of the function. + +Switches in Go do not have automatic fallthrough, unlike languages +like C. It is not necessary to have a break statement as the final +statement in a case block.`, + Since: "2017.1", + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + fn1 := func(node ast.Node) { + clause := node.(*ast.CaseClause) + if len(clause.Body) < 2 { + return + } + branch, ok := clause.Body[len(clause.Body)-1].(*ast.BranchStmt) + if !ok || branch.Tok != token.BREAK || branch.Label != nil { + return + } + report.Report(pass, branch, "redundant break statement", report.FilterGenerated()) + } + fn2 := func(node ast.Node) { + var ret *ast.FieldList + var body *ast.BlockStmt + switch x := node.(type) { + case *ast.FuncDecl: + ret = x.Type.Results + body = x.Body + case *ast.FuncLit: + ret = x.Type.Results + body = x.Body + default: + lint.ExhaustiveTypeSwitch(node) + } + // if the func has results, a return can't be redundant. + // similarly, if there are no statements, there can be + // no return. + if ret != nil || body == nil || len(body.List) < 1 { + return + } + rst, ok := body.List[len(body.List)-1].(*ast.ReturnStmt) + if !ok { + return + } + // we don't need to check rst.Results as we already + // checked x.Type.Results to be nil. + report.Report(pass, rst, "redundant return statement", report.FilterGenerated()) + } + code.Preorder(pass, fn1, (*ast.CaseClause)(nil)) + code.Preorder(pass, fn2, (*ast.FuncDecl)(nil), (*ast.FuncLit)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/simple/s1024/s1024.go b/tools/vendor/honnef.co/go/tools/simple/s1024/s1024.go new file mode 100644 index 0000000000..dbf8940b37 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/simple/s1024/s1024.go @@ -0,0 +1,59 @@ +package s1024 + +import ( + "go/ast" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/edit" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "S1024", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Replace \'x.Sub(time.Now())\' with \'time.Until(x)\'`, + Text: `The \'time.Until\' helper has the same effect as using \'x.Sub(time.Now())\' +but is easier to read.`, + Before: `x.Sub(time.Now())`, + After: `time.Until(x)`, + Since: "2017.1", + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var ( + checkTimeUntilQ = pattern.MustParse(`(CallExpr (Symbol "(time.Time).Sub") [(CallExpr (Symbol "time.Now") [])])`) + checkTimeUntilR = pattern.MustParse(`(CallExpr (SelectorExpr (Ident "time") (Ident "Until")) [arg])`) +) + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + if _, ok := code.Match(pass, checkTimeUntilQ, node); ok { + if sel, ok := node.(*ast.CallExpr).Fun.(*ast.SelectorExpr); ok { + r := pattern.NodeToAST(checkTimeUntilR.Root, map[string]interface{}{"arg": sel.X}).(ast.Node) + report.Report(pass, node, "should use time.Until instead of t.Sub(time.Now())", + report.FilterGenerated(), + report.MinimumStdlibVersion("go1.8"), + report.Fixes(edit.Fix("replace with call to time.Until", edit.ReplaceWithNode(pass.Fset, node, r)))) + } else { + report.Report(pass, node, "should use time.Until instead of t.Sub(time.Now())", + report.MinimumStdlibVersion("go1.8"), + report.FilterGenerated()) + } + } + } + code.Preorder(pass, fn, (*ast.CallExpr)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/simple/s1025/s1025.go b/tools/vendor/honnef.co/go/tools/simple/s1025/s1025.go new file mode 100644 index 0000000000..3ea2996d1e --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/simple/s1025/s1025.go @@ -0,0 +1,158 @@ +package s1025 + +import ( + "go/ast" + "go/types" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/edit" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/types/typeutil" + "honnef.co/go/tools/internal/passes/buildir" + "honnef.co/go/tools/knowledge" + "honnef.co/go/tools/pattern" + + "golang.org/x/exp/typeparams" + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "S1025", + Run: run, + Requires: []*analysis.Analyzer{buildir.Analyzer, inspect.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Don't use \'fmt.Sprintf("%s", x)\' unnecessarily`, + Text: `In many instances, there are easier and more efficient ways of getting +a value's string representation. Whenever a value's underlying type is +a string already, or the type has a String method, they should be used +directly. + +Given the following shared definitions + + type T1 string + type T2 int + + func (T2) String() string { return "Hello, world" } + + var x string + var y T1 + var z T2 + +we can simplify + + fmt.Sprintf("%s", x) + fmt.Sprintf("%s", y) + fmt.Sprintf("%s", z) + +to + + x + string(y) + z.String() +`, + Since: "2017.1", + MergeIf: lint.MergeIfAll, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var checkRedundantSprintfQ = pattern.MustParse(`(CallExpr (Symbol "fmt.Sprintf") [format arg])`) + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + m, ok := code.Match(pass, checkRedundantSprintfQ, node) + if !ok { + return + } + + format := m.State["format"].(ast.Expr) + arg := m.State["arg"].(ast.Expr) + // TODO(dh): should we really support named constants here? + // shouldn't we only look for string literals? to avoid false + // positives via build tags? + if s, ok := code.ExprToString(pass, format); !ok || s != "%s" { + return + } + typ := pass.TypesInfo.TypeOf(arg) + if typeparams.IsTypeParam(typ) { + return + } + irpkg := pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg + + if typeutil.IsTypeWithName(typ, "reflect.Value") { + // printing with %s produces output different from using + // the String method + return + } + + if isFormatter(typ, &irpkg.Prog.MethodSets) { + // the type may choose to handle %s in arbitrary ways + return + } + + if types.Implements(typ, knowledge.Interfaces["fmt.Stringer"]) { + replacement := &ast.CallExpr{ + Fun: &ast.SelectorExpr{ + X: arg, + Sel: &ast.Ident{Name: "String"}, + }, + } + report.Report(pass, node, "should use String() instead of fmt.Sprintf", + report.Fixes(edit.Fix("replace with call to String method", edit.ReplaceWithNode(pass.Fset, node, replacement)))) + } else if types.Unalias(typ) == types.Universe.Lookup("string").Type() { + report.Report(pass, node, "the argument is already a string, there's no need to use fmt.Sprintf", + report.FilterGenerated(), + report.Fixes(edit.Fix("remove unnecessary call to fmt.Sprintf", edit.ReplaceWithNode(pass.Fset, node, arg)))) + } else if typ.Underlying() == types.Universe.Lookup("string").Type() { + replacement := &ast.CallExpr{ + Fun: &ast.Ident{Name: "string"}, + Args: []ast.Expr{arg}, + } + report.Report(pass, node, "the argument's underlying type is a string, should use a simple conversion instead of fmt.Sprintf", + report.FilterGenerated(), + report.Fixes(edit.Fix("replace with conversion to string", edit.ReplaceWithNode(pass.Fset, node, replacement)))) + } else if code.IsOfStringConvertibleByteSlice(pass, arg) { + replacement := &ast.CallExpr{ + Fun: &ast.Ident{Name: "string"}, + Args: []ast.Expr{arg}, + } + report.Report(pass, node, "the argument's underlying type is a slice of bytes, should use a simple conversion instead of fmt.Sprintf", + report.FilterGenerated(), + report.Fixes(edit.Fix("replace with conversion to string", edit.ReplaceWithNode(pass.Fset, node, replacement)))) + } + + } + code.Preorder(pass, fn, (*ast.CallExpr)(nil)) + return nil, nil +} + +func isFormatter(T types.Type, msCache *typeutil.MethodSetCache) bool { + // TODO(dh): this function also exists in staticcheck/lint.go – deduplicate. + + ms := msCache.MethodSet(T) + sel := ms.Lookup(nil, "Format") + if sel == nil { + return false + } + fn, ok := sel.Obj().(*types.Func) + if !ok { + // should be unreachable + return false + } + sig := fn.Type().(*types.Signature) + if sig.Params().Len() != 2 { + return false + } + // TODO(dh): check the types of the arguments for more + // precision + if sig.Results().Len() != 0 { + return false + } + return true +} diff --git a/tools/vendor/honnef.co/go/tools/simple/s1028/s1028.go b/tools/vendor/honnef.co/go/tools/simple/s1028/s1028.go new file mode 100644 index 0000000000..2d2588e236 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/simple/s1028/s1028.go @@ -0,0 +1,50 @@ +package s1028 + +import ( + "go/ast" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/edit" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "S1028", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Simplify error construction with \'fmt.Errorf\'`, + Before: `errors.New(fmt.Sprintf(...))`, + After: `fmt.Errorf(...)`, + Since: "2017.1", + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var ( + checkErrorsNewSprintfQ = pattern.MustParse(`(CallExpr (Symbol "errors.New") [(CallExpr (Symbol "fmt.Sprintf") args)])`) + checkErrorsNewSprintfR = pattern.MustParse(`(CallExpr (SelectorExpr (Ident "fmt") (Ident "Errorf")) args)`) +) + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + if _, edits, ok := code.MatchAndEdit(pass, checkErrorsNewSprintfQ, checkErrorsNewSprintfR, node); ok { + // TODO(dh): the suggested fix may leave an unused import behind + report.Report(pass, node, "should use fmt.Errorf(...) instead of errors.New(fmt.Sprintf(...))", + report.FilterGenerated(), + report.Fixes(edit.Fix("use fmt.Errorf", edits...))) + } + } + code.Preorder(pass, fn, (*ast.CallExpr)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/simple/s1029/s1029.go b/tools/vendor/honnef.co/go/tools/simple/s1029/s1029.go new file mode 100644 index 0000000000..1bf3f00979 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/simple/s1029/s1029.go @@ -0,0 +1,31 @@ +package s1029 + +import ( + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/internal/passes/buildir" + "honnef.co/go/tools/internal/sharedcheck" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "S1029", + Run: sharedcheck.CheckRangeStringRunes, + Requires: []*analysis.Analyzer{buildir.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Range over the string directly`, + Text: `Ranging over a string will yield byte offsets and runes. If the offset +isn't used, this is functionally equivalent to converting the string +to a slice of runes and ranging over that. Ranging directly over the +string will be more performant, however, as it avoids allocating a new +slice, the size of which depends on the length of the string.`, + Before: `for _, r := range []rune(s) {}`, + After: `for _, r := range s {}`, + Since: "2017.1", + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer diff --git a/tools/vendor/honnef.co/go/tools/simple/s1030/s1030.go b/tools/vendor/honnef.co/go/tools/simple/s1030/s1030.go new file mode 100644 index 0000000000..4adf8d8b51 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/simple/s1030/s1030.go @@ -0,0 +1,83 @@ +package s1030 + +import ( + "fmt" + "go/ast" + "go/types" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/edit" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "S1030", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Use \'bytes.Buffer.String\' or \'bytes.Buffer.Bytes\'`, + Text: `\'bytes.Buffer\' has both a \'String\' and a \'Bytes\' method. It is almost never +necessary to use \'string(buf.Bytes())\' or \'[]byte(buf.String())\' – simply +use the other method. + +The only exception to this are map lookups. Due to a compiler optimization, +\'m[string(buf.Bytes())]\' is more efficient than \'m[buf.String()]\'. +`, + Since: "2017.1", + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var ( + checkBytesBufferConversionsQ = pattern.MustParse(`(CallExpr _ [(CallExpr sel@(SelectorExpr recv _) [])])`) + checkBytesBufferConversionsRs = pattern.MustParse(`(CallExpr (SelectorExpr recv (Ident "String")) [])`) + checkBytesBufferConversionsRb = pattern.MustParse(`(CallExpr (SelectorExpr recv (Ident "Bytes")) [])`) +) + +func run(pass *analysis.Pass) (interface{}, error) { + if pass.Pkg.Path() == "bytes" || pass.Pkg.Path() == "bytes_test" { + // The bytes package can use itself however it wants + return nil, nil + } + fn := func(node ast.Node, stack []ast.Node) { + m, ok := code.Match(pass, checkBytesBufferConversionsQ, node) + if !ok { + return + } + call := node.(*ast.CallExpr) + sel := m.State["sel"].(*ast.SelectorExpr) + + typ := pass.TypesInfo.TypeOf(call.Fun) + if types.Unalias(typ) == types.Universe.Lookup("string").Type() && code.IsCallTo(pass, call.Args[0], "(*bytes.Buffer).Bytes") { + if _, ok := stack[len(stack)-2].(*ast.IndexExpr); ok { + // Don't flag m[string(buf.Bytes())] – thanks to a + // compiler optimization, this is actually faster than + // m[buf.String()] + return + } + + report.Report(pass, call, fmt.Sprintf("should use %v.String() instead of %v", report.Render(pass, sel.X), report.Render(pass, call)), + report.FilterGenerated(), + report.Fixes(edit.Fix("simplify conversion", edit.ReplaceWithPattern(pass.Fset, node, checkBytesBufferConversionsRs, m.State)))) + } else if typ, ok := types.Unalias(typ).(*types.Slice); ok && + types.Unalias(typ.Elem()) == types.Universe.Lookup("byte").Type() && + code.IsCallTo(pass, call.Args[0], "(*bytes.Buffer).String") { + report.Report(pass, call, fmt.Sprintf("should use %v.Bytes() instead of %v", report.Render(pass, sel.X), report.Render(pass, call)), + report.FilterGenerated(), + report.Fixes(edit.Fix("simplify conversion", edit.ReplaceWithPattern(pass.Fset, node, checkBytesBufferConversionsRb, m.State)))) + } + + } + code.PreorderStack(pass, fn, (*ast.CallExpr)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/simple/s1031/s1031.go b/tools/vendor/honnef.co/go/tools/simple/s1031/s1031.go new file mode 100644 index 0000000000..e1ce6aa04b --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/simple/s1031/s1031.go @@ -0,0 +1,79 @@ +package s1031 + +import ( + "go/ast" + "go/types" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/types/typeutil" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "S1031", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Omit redundant nil check around loop`, + Text: `You can use range on nil slices and maps, the loop will simply never +execute. This makes an additional nil check around the loop +unnecessary.`, + Before: ` +if s != nil { + for _, x := range s { + ... + } +}`, + After: ` +for _, x := range s { + ... +}`, + Since: "2017.1", + // MergeIfAll because x might be a channel under some build tags. + // you shouldn't write code like that… + MergeIf: lint.MergeIfAll, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var checkNilCheckAroundRangeQ = pattern.MustParse(` + (IfStmt + nil + (BinaryExpr x@(Object _) "!=" (Builtin "nil")) + [(RangeStmt _ _ _ x _)] + nil)`) + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + m, ok := code.Match(pass, checkNilCheckAroundRangeQ, node) + if !ok { + return + } + ok = typeutil.All(m.State["x"].(types.Object).Type(), func(term *types.Term) bool { + switch term.Type().Underlying().(type) { + case *types.Slice, *types.Map: + return true + case *types.TypeParam, *types.Chan, *types.Pointer, *types.Signature: + return false + default: + lint.ExhaustiveTypeSwitch(term.Type().Underlying()) + return false + } + }) + if !ok { + return + } + report.Report(pass, node, "unnecessary nil check around range", report.ShortRange(), report.FilterGenerated()) + } + code.Preorder(pass, fn, (*ast.IfStmt)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/simple/s1032/s1032.go b/tools/vendor/honnef.co/go/tools/simple/s1032/s1032.go new file mode 100644 index 0000000000..7a0873cca9 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/simple/s1032/s1032.go @@ -0,0 +1,128 @@ +package s1032 + +import ( + "go/ast" + "go/token" + "sort" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/knowledge" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "S1032", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Use \'sort.Ints(x)\', \'sort.Float64s(x)\', and \'sort.Strings(x)\'`, + Text: `The \'sort.Ints\', \'sort.Float64s\' and \'sort.Strings\' functions are easier to +read than \'sort.Sort(sort.IntSlice(x))\', \'sort.Sort(sort.Float64Slice(x))\' +and \'sort.Sort(sort.StringSlice(x))\'.`, + Before: `sort.Sort(sort.StringSlice(x))`, + After: `sort.Strings(x)`, + Since: "2019.1", + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func isPermissibleSort(pass *analysis.Pass, node ast.Node) bool { + call := node.(*ast.CallExpr) + typeconv, ok := call.Args[0].(*ast.CallExpr) + if !ok { + return true + } + + sel, ok := typeconv.Fun.(*ast.SelectorExpr) + if !ok { + return true + } + name := code.SelectorName(pass, sel) + switch name { + case "sort.IntSlice", "sort.Float64Slice", "sort.StringSlice": + default: + return true + } + + return false +} + +func run(pass *analysis.Pass) (interface{}, error) { + type Error struct { + node ast.Node + msg string + } + var allErrors []Error + fn := func(node ast.Node) { + var body *ast.BlockStmt + switch node := node.(type) { + case *ast.FuncLit: + body = node.Body + case *ast.FuncDecl: + body = node.Body + default: + lint.ExhaustiveTypeSwitch(node) + } + if body == nil { + return + } + + var errors []Error + permissible := false + fnSorts := func(node ast.Node) bool { + if permissible { + return false + } + if !code.IsCallTo(pass, node, "sort.Sort") { + return true + } + if isPermissibleSort(pass, node) { + permissible = true + return false + } + call := node.(*ast.CallExpr) + // isPermissibleSort guarantees that this type assertion will succeed + typeconv := call.Args[knowledge.Arg("sort.Sort.data")].(*ast.CallExpr) + sel := typeconv.Fun.(*ast.SelectorExpr) + name := code.SelectorName(pass, sel) + + switch name { + case "sort.IntSlice": + errors = append(errors, Error{node, "should use sort.Ints(...) instead of sort.Sort(sort.IntSlice(...))"}) + case "sort.Float64Slice": + errors = append(errors, Error{node, "should use sort.Float64s(...) instead of sort.Sort(sort.Float64Slice(...))"}) + case "sort.StringSlice": + errors = append(errors, Error{node, "should use sort.Strings(...) instead of sort.Sort(sort.StringSlice(...))"}) + } + return true + } + ast.Inspect(body, fnSorts) + + if permissible { + return + } + allErrors = append(allErrors, errors...) + } + code.Preorder(pass, fn, (*ast.FuncLit)(nil), (*ast.FuncDecl)(nil)) + sort.Slice(allErrors, func(i, j int) bool { + return allErrors[i].node.Pos() < allErrors[j].node.Pos() + }) + var prev token.Pos + for _, err := range allErrors { + if err.node.Pos() == prev { + continue + } + prev = err.node.Pos() + report.Report(pass, err.node, err.msg, report.FilterGenerated()) + } + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/simple/s1033/s1033.go b/tools/vendor/honnef.co/go/tools/simple/s1033/s1033.go new file mode 100644 index 0000000000..bf28952986 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/simple/s1033/s1033.go @@ -0,0 +1,55 @@ +package s1033 + +import ( + "go/ast" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/edit" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "S1033", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Unnecessary guard around call to \"delete\"`, + Text: `Calling \'delete\' on a nil map is a no-op.`, + Since: "2019.2", + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var checkGuardedDeleteQ = pattern.MustParse(` + (IfStmt + (AssignStmt + [(Ident "_") ok@(Ident _)] + ":=" + (IndexExpr m key)) + ok + [call@(CallExpr (Builtin "delete") [m key])] + nil)`) + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + if m, ok := code.Match(pass, checkGuardedDeleteQ, node); ok { + report.Report(pass, node, "unnecessary guard around call to delete", + report.ShortRange(), + report.FilterGenerated(), + report.Fixes(edit.Fix("remove guard", edit.ReplaceWithNode(pass.Fset, node, m.State["call"].(ast.Node))))) + } + } + + code.Preorder(pass, fn, (*ast.IfStmt)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/simple/s1034/s1034.go b/tools/vendor/honnef.co/go/tools/simple/s1034/s1034.go new file mode 100644 index 0000000000..e561dfe4f0 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/simple/s1034/s1034.go @@ -0,0 +1,119 @@ +package s1034 + +import ( + "fmt" + "go/ast" + "go/types" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/edit" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "S1034", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Use result of type assertion to simplify cases`, + Since: "2019.2", + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var ( + checkSimplifyTypeSwitchQ = pattern.MustParse(` + (TypeSwitchStmt + nil + expr@(TypeAssertExpr ident@(Ident _) _) + body)`) + checkSimplifyTypeSwitchR = pattern.MustParse(`(AssignStmt ident ":=" expr)`) +) + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + m, ok := code.Match(pass, checkSimplifyTypeSwitchQ, node) + if !ok { + return + } + stmt := node.(*ast.TypeSwitchStmt) + expr := m.State["expr"].(ast.Node) + ident := m.State["ident"].(*ast.Ident) + + x := pass.TypesInfo.ObjectOf(ident) + var allOffenders []*ast.TypeAssertExpr + canSuggestFix := true + for _, clause := range stmt.Body.List { + clause := clause.(*ast.CaseClause) + if len(clause.List) != 1 { + continue + } + hasUnrelatedAssertion := false + var offenders []*ast.TypeAssertExpr + ast.Inspect(clause, func(node ast.Node) bool { + assert2, ok := node.(*ast.TypeAssertExpr) + if !ok { + return true + } + ident, ok := assert2.X.(*ast.Ident) + if !ok { + hasUnrelatedAssertion = true + return false + } + if pass.TypesInfo.ObjectOf(ident) != x { + hasUnrelatedAssertion = true + return false + } + + if !types.Identical(pass.TypesInfo.TypeOf(clause.List[0]), pass.TypesInfo.TypeOf(assert2.Type)) { + hasUnrelatedAssertion = true + return false + } + offenders = append(offenders, assert2) + return true + }) + if !hasUnrelatedAssertion { + // don't flag cases that have other type assertions + // unrelated to the one in the case clause. often + // times, this is done for symmetry, when two + // different values have to be asserted to the same + // type. + allOffenders = append(allOffenders, offenders...) + } + canSuggestFix = canSuggestFix && !hasUnrelatedAssertion + } + if len(allOffenders) != 0 { + var opts []report.Option + for _, offender := range allOffenders { + opts = append(opts, report.Related(offender, "could eliminate this type assertion")) + } + opts = append(opts, report.FilterGenerated()) + + msg := fmt.Sprintf("assigning the result of this type assertion to a variable (switch %s := %s.(type)) could eliminate type assertions in switch cases", + report.Render(pass, ident), report.Render(pass, ident)) + if canSuggestFix { + var edits []analysis.TextEdit + edits = append(edits, edit.ReplaceWithPattern(pass.Fset, expr, checkSimplifyTypeSwitchR, m.State)) + for _, offender := range allOffenders { + edits = append(edits, edit.ReplaceWithNode(pass.Fset, offender, offender.X)) + } + opts = append(opts, report.Fixes(edit.Fix("simplify type switch", edits...))) + report.Report(pass, expr, msg, opts...) + } else { + report.Report(pass, expr, msg, opts...) + } + } + } + code.Preorder(pass, fn, (*ast.TypeSwitchStmt)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/simple/s1035/s1035.go b/tools/vendor/honnef.co/go/tools/simple/s1035/s1035.go new file mode 100644 index 0000000000..ed5faf7576 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/simple/s1035/s1035.go @@ -0,0 +1,56 @@ +package s1035 + +import ( + "fmt" + "go/ast" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/edit" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "S1035", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Redundant call to \'net/http.CanonicalHeaderKey\' in method call on \'net/http.Header\'`, + Text: ` +The methods on \'net/http.Header\', namely \'Add\', \'Del\', \'Get\' +and \'Set\', already canonicalize the given header name.`, + Since: "2020.1", + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + call := node.(*ast.CallExpr) + callName := code.CallName(pass, call) + switch callName { + case "(net/http.Header).Add", "(net/http.Header).Del", "(net/http.Header).Get", "(net/http.Header).Set": + default: + return + } + + if !code.IsCallTo(pass, call.Args[0], "net/http.CanonicalHeaderKey") { + return + } + + report.Report(pass, call, + fmt.Sprintf("calling net/http.CanonicalHeaderKey on the 'key' argument of %s is redundant", callName), + report.FilterGenerated(), + report.Fixes(edit.Fix("remove call to CanonicalHeaderKey", edit.ReplaceWithNode(pass.Fset, call.Args[0], call.Args[0].(*ast.CallExpr).Args[0])))) + } + code.Preorder(pass, fn, (*ast.CallExpr)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/simple/s1036/s1036.go b/tools/vendor/honnef.co/go/tools/simple/s1036/s1036.go new file mode 100644 index 0000000000..ea675b13cd --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/simple/s1036/s1036.go @@ -0,0 +1,92 @@ +package s1036 + +import ( + "go/ast" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/edit" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "S1036", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Unnecessary guard around map access`, + + Text: ` +When accessing a map key that doesn't exist yet, one receives a zero +value. Often, the zero value is a suitable value, for example when +using append or doing integer math. + +The following + + if _, ok := m["foo"]; ok { + m["foo"] = append(m["foo"], "bar") + } else { + m["foo"] = []string{"bar"} + } + +can be simplified to + + m["foo"] = append(m["foo"], "bar") + +and + + if _, ok := m2["k"]; ok { + m2["k"] += 4 + } else { + m2["k"] = 4 + } + +can be simplified to + + m["k"] += 4 +`, + Since: "2020.1", + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var checkUnnecessaryGuardQ = pattern.MustParse(` + (Or + (IfStmt + (AssignStmt [(Ident "_") ok@(Ident _)] ":=" indexexpr@(IndexExpr _ _)) + ok + set@(AssignStmt indexexpr "=" (CallExpr (Builtin "append") indexexpr:values)) + (AssignStmt indexexpr "=" (CompositeLit _ values))) + (IfStmt + (AssignStmt [(Ident "_") ok] ":=" indexexpr@(IndexExpr _ _)) + ok + set@(AssignStmt indexexpr "+=" value) + (AssignStmt indexexpr "=" value)) + (IfStmt + (AssignStmt [(Ident "_") ok] ":=" indexexpr@(IndexExpr _ _)) + ok + set@(IncDecStmt indexexpr "++") + (AssignStmt indexexpr "=" (IntegerLiteral "1"))))`) + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + if m, ok := code.Match(pass, checkUnnecessaryGuardQ, node); ok { + if code.MayHaveSideEffects(pass, m.State["indexexpr"].(ast.Expr), nil) { + return + } + report.Report(pass, node, "unnecessary guard around map access", + report.ShortRange(), + report.Fixes(edit.Fix("simplify map access", edit.ReplaceWithNode(pass.Fset, node, m.State["set"].(ast.Node))))) + } + } + code.Preorder(pass, fn, (*ast.IfStmt)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/simple/s1037/s1037.go b/tools/vendor/honnef.co/go/tools/simple/s1037/s1037.go new file mode 100644 index 0000000000..a72d42ccc4 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/simple/s1037/s1037.go @@ -0,0 +1,59 @@ +package s1037 + +import ( + "go/ast" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/edit" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "S1037", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Elaborate way of sleeping`, + Text: `Using a select statement with a single case receiving +from the result of \'time.After\' is a very elaborate way of sleeping that +can much simpler be expressed with a simple call to time.Sleep.`, + Since: "2020.1", + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var ( + checkElaborateSleepQ = pattern.MustParse(`(SelectStmt (CommClause (UnaryExpr "<-" (CallExpr (Symbol "time.After") [arg])) body))`) + checkElaborateSleepR = pattern.MustParse(`(CallExpr (SelectorExpr (Ident "time") (Ident "Sleep")) [arg])`) +) + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + if m, ok := code.Match(pass, checkElaborateSleepQ, node); ok { + if body, ok := m.State["body"].([]ast.Stmt); ok && len(body) == 0 { + report.Report(pass, node, "should use time.Sleep instead of elaborate way of sleeping", + report.ShortRange(), + report.FilterGenerated(), + report.Fixes(edit.Fix("Use time.Sleep", edit.ReplaceWithPattern(pass.Fset, node, checkElaborateSleepR, m.State)))) + } else { + // TODO(dh): we could make a suggested fix if the body + // doesn't declare or shadow any identifiers + report.Report(pass, node, "should use time.Sleep instead of elaborate way of sleeping", + report.ShortRange(), + report.FilterGenerated()) + } + } + } + code.Preorder(pass, fn, (*ast.SelectStmt)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/simple/s1038/s1038.go b/tools/vendor/honnef.co/go/tools/simple/s1038/s1038.go new file mode 100644 index 0000000000..301b901643 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/simple/s1038/s1038.go @@ -0,0 +1,188 @@ +package s1038 + +import ( + "fmt" + "go/ast" + "go/types" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/types/typeutil" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "S1038", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: "Unnecessarily complex way of printing formatted string", + Text: `Instead of using \'fmt.Print(fmt.Sprintf(...))\', one can use \'fmt.Printf(...)\'.`, + Since: "2020.1", + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var ( + checkPrintSprintQ = pattern.MustParse(` + (Or + (CallExpr + fn@(Or + (Symbol "fmt.Print") + (Symbol "fmt.Sprint") + (Symbol "fmt.Println") + (Symbol "fmt.Sprintln")) + [(CallExpr (Symbol "fmt.Sprintf") f:_)]) + (CallExpr + fn@(Or + (Symbol "fmt.Fprint") + (Symbol "fmt.Fprintln")) + [_ (CallExpr (Symbol "fmt.Sprintf") f:_)]))`) + + checkTestingErrorSprintfQ = pattern.MustParse(` + (CallExpr + sel@(SelectorExpr + recv + (Ident + name@(Or + "Error" + "Fatal" + "Fatalln" + "Log" + "Panic" + "Panicln" + "Print" + "Println" + "Skip"))) + [(CallExpr (Symbol "fmt.Sprintf") args)])`) + + checkLogSprintfQ = pattern.MustParse(` + (CallExpr + (Symbol + (Or + "log.Fatal" + "log.Fatalln" + "log.Panic" + "log.Panicln" + "log.Print" + "log.Println")) + [(CallExpr (Symbol "fmt.Sprintf") args)])`) + + checkSprintfMapping = map[string]struct { + recv string + alternative string + }{ + "(*testing.common).Error": {"(*testing.common)", "Errorf"}, + "(testing.TB).Error": {"(testing.TB)", "Errorf"}, + "(*testing.common).Fatal": {"(*testing.common)", "Fatalf"}, + "(testing.TB).Fatal": {"(testing.TB)", "Fatalf"}, + "(*testing.common).Log": {"(*testing.common)", "Logf"}, + "(testing.TB).Log": {"(testing.TB)", "Logf"}, + "(*testing.common).Skip": {"(*testing.common)", "Skipf"}, + "(testing.TB).Skip": {"(testing.TB)", "Skipf"}, + "(*log.Logger).Fatal": {"(*log.Logger)", "Fatalf"}, + "(*log.Logger).Fatalln": {"(*log.Logger)", "Fatalf"}, + "(*log.Logger).Panic": {"(*log.Logger)", "Panicf"}, + "(*log.Logger).Panicln": {"(*log.Logger)", "Panicf"}, + "(*log.Logger).Print": {"(*log.Logger)", "Printf"}, + "(*log.Logger).Println": {"(*log.Logger)", "Printf"}, + "log.Fatal": {"", "log.Fatalf"}, + "log.Fatalln": {"", "log.Fatalf"}, + "log.Panic": {"", "log.Panicf"}, + "log.Panicln": {"", "log.Panicf"}, + "log.Print": {"", "log.Printf"}, + "log.Println": {"", "log.Printf"}, + } +) + +func run(pass *analysis.Pass) (interface{}, error) { + fmtPrintf := func(node ast.Node) { + m, ok := code.Match(pass, checkPrintSprintQ, node) + if !ok { + return + } + + name := m.State["fn"].(*types.Func).Name() + var msg string + switch name { + case "Print", "Fprint", "Sprint": + newname := name + "f" + msg = fmt.Sprintf("should use fmt.%s instead of fmt.%s(fmt.Sprintf(...))", newname, name) + case "Println", "Fprintln", "Sprintln": + if _, ok := m.State["f"].(*ast.BasicLit); !ok { + // This may be an instance of + // fmt.Println(fmt.Sprintf(arg, ...)) where arg is an + // externally provided format string and the caller + // cannot guarantee that the format string ends with a + // newline. + return + } + newname := name[:len(name)-2] + "f" + msg = fmt.Sprintf("should use fmt.%s instead of fmt.%s(fmt.Sprintf(...)) (but don't forget the newline)", newname, name) + } + report.Report(pass, node, msg, + report.FilterGenerated()) + } + + methSprintf := func(node ast.Node) { + m, ok := code.Match(pass, checkTestingErrorSprintfQ, node) + if !ok { + return + } + mapped, ok := checkSprintfMapping[code.CallName(pass, node.(*ast.CallExpr))] + if !ok { + return + } + + // Ensure that Errorf/Fatalf refer to the right method + recvTV, ok := pass.TypesInfo.Types[m.State["recv"].(ast.Expr)] + if !ok { + return + } + obj, _, _ := types.LookupFieldOrMethod(recvTV.Type, recvTV.Addressable(), nil, mapped.alternative) + f, ok := obj.(*types.Func) + if !ok { + return + } + if typeutil.FuncName(f) != mapped.recv+"."+mapped.alternative { + return + } + + alt := &ast.SelectorExpr{ + X: m.State["recv"].(ast.Expr), + Sel: &ast.Ident{Name: mapped.alternative}, + } + report.Report(pass, node, fmt.Sprintf("should use %s(...) instead of %s(fmt.Sprintf(...))", report.Render(pass, alt), report.Render(pass, m.State["sel"].(*ast.SelectorExpr)))) + } + + pkgSprintf := func(node ast.Node) { + _, ok := code.Match(pass, checkLogSprintfQ, node) + if !ok { + return + } + callName := code.CallName(pass, node.(*ast.CallExpr)) + mapped, ok := checkSprintfMapping[callName] + if !ok { + return + } + report.Report(pass, node, fmt.Sprintf("should use %s(...) instead of %s(fmt.Sprintf(...))", mapped.alternative, callName)) + } + + fn := func(node ast.Node) { + fmtPrintf(node) + // TODO(dh): add suggested fixes + methSprintf(node) + pkgSprintf(node) + } + code.Preorder(pass, fn, (*ast.CallExpr)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/simple/s1039/s1039.go b/tools/vendor/honnef.co/go/tools/simple/s1039/s1039.go new file mode 100644 index 0000000000..24eef6a11a --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/simple/s1039/s1039.go @@ -0,0 +1,71 @@ +package s1039 + +import ( + "fmt" + "go/ast" + "go/types" + "strings" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/edit" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "S1039", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Unnecessary use of \'fmt.Sprint\'`, + Text: ` +Calling \'fmt.Sprint\' with a single string argument is unnecessary +and identical to using the string directly.`, + Since: "2020.1", + // MergeIfAll because s might not be a string under all build tags. + // you shouldn't write code like that… + MergeIf: lint.MergeIfAll, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var checkSprintLiteralQ = pattern.MustParse(` + (CallExpr + fn@(Or + (Symbol "fmt.Sprint") + (Symbol "fmt.Sprintf")) + [lit@(BasicLit "STRING" _)])`) + +func run(pass *analysis.Pass) (interface{}, error) { + // We only flag calls with string literals, not expressions of + // type string, because some people use fmt.Sprint(s) as a pattern + // for copying strings, which may be useful when extracting a small + // substring from a large string. + fn := func(node ast.Node) { + m, ok := code.Match(pass, checkSprintLiteralQ, node) + if !ok { + return + } + callee := m.State["fn"].(*types.Func) + lit := m.State["lit"].(*ast.BasicLit) + if callee.Name() == "Sprintf" { + if strings.ContainsRune(lit.Value, '%') { + // This might be a format string + return + } + } + report.Report(pass, node, fmt.Sprintf("unnecessary use of fmt.%s", callee.Name()), + report.FilterGenerated(), + report.Fixes(edit.Fix("Replace with string literal", edit.ReplaceWithNode(pass.Fset, node, lit)))) + } + code.Preorder(pass, fn, (*ast.CallExpr)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/simple/s1040/s1040.go b/tools/vendor/honnef.co/go/tools/simple/s1040/s1040.go new file mode 100644 index 0000000000..d13dae4d47 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/simple/s1040/s1040.go @@ -0,0 +1,73 @@ +package s1040 + +import ( + "fmt" + "go/ast" + "go/types" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "S1040", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: "Type assertion to current type", + Text: `The type assertion \'x.(SomeInterface)\', when \'x\' already has type +\'SomeInterface\', can only fail if \'x\' is nil. Usually, this is +left-over code from when \'x\' had a different type and you can safely +delete the type assertion. If you want to check that \'x\' is not nil, +consider being explicit and using an actual \'if x == nil\' comparison +instead of relying on the type assertion panicking.`, + Since: "2021.1", + // MergeIfAll because x might have different types under different build tags. + // You shouldn't write code like that… + MergeIf: lint.MergeIfAll, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + expr := node.(*ast.TypeAssertExpr) + if expr.Type == nil { + // skip type switches + // + // TODO(dh): we could flag type switches, too, when a case + // statement has the same type as expr.X – however, + // depending on the location of that case, it might behave + // identically to a default branch. we need to think + // carefully about the instances we want to flag. We also + // have to take nil interface values into consideration. + // + // It might make more sense to extend SA4020 to handle + // this. + return + } + t1 := pass.TypesInfo.TypeOf(expr.Type) + t2 := pass.TypesInfo.TypeOf(expr.X) + if types.IsInterface(t1) && types.Identical(t1, t2) { + report.Report(pass, expr, + fmt.Sprintf("type assertion to the same type: %s already has type %s", report.Render(pass, expr.X), report.Render(pass, expr.Type)), + report.FilterGenerated()) + } + } + + // TODO(dh): add suggested fixes. we need different fixes depending on the context: + // - assignment with 1 or 2 lhs + // - assignment to blank identifiers (as the first, second or both lhs) + // - initializers in if statements, with the same variations as above + + code.Preorder(pass, fn, (*ast.TypeAssertExpr)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/analysis.go b/tools/vendor/honnef.co/go/tools/staticcheck/analysis.go index 210c348c9c..fe8642abaa 100644 --- a/tools/vendor/honnef.co/go/tools/staticcheck/analysis.go +++ b/tools/vendor/honnef.co/go/tools/staticcheck/analysis.go @@ -1,330 +1,200 @@ +// Code generated by generate.go. DO NOT EDIT. + package staticcheck import ( - "honnef.co/go/tools/analysis/facts/deprecated" - "honnef.co/go/tools/analysis/facts/generated" - "honnef.co/go/tools/analysis/facts/nilness" - "honnef.co/go/tools/analysis/facts/purity" - "honnef.co/go/tools/analysis/facts/tokenfile" - "honnef.co/go/tools/analysis/facts/typedness" "honnef.co/go/tools/analysis/lint" - "honnef.co/go/tools/internal/passes/buildir" - - "golang.org/x/tools/go/analysis" - "golang.org/x/tools/go/analysis/passes/inspect" + "honnef.co/go/tools/staticcheck/sa1000" + "honnef.co/go/tools/staticcheck/sa1001" + "honnef.co/go/tools/staticcheck/sa1002" + "honnef.co/go/tools/staticcheck/sa1003" + "honnef.co/go/tools/staticcheck/sa1004" + "honnef.co/go/tools/staticcheck/sa1005" + "honnef.co/go/tools/staticcheck/sa1006" + "honnef.co/go/tools/staticcheck/sa1007" + "honnef.co/go/tools/staticcheck/sa1008" + "honnef.co/go/tools/staticcheck/sa1010" + "honnef.co/go/tools/staticcheck/sa1011" + "honnef.co/go/tools/staticcheck/sa1012" + "honnef.co/go/tools/staticcheck/sa1013" + "honnef.co/go/tools/staticcheck/sa1014" + "honnef.co/go/tools/staticcheck/sa1015" + "honnef.co/go/tools/staticcheck/sa1016" + "honnef.co/go/tools/staticcheck/sa1017" + "honnef.co/go/tools/staticcheck/sa1018" + "honnef.co/go/tools/staticcheck/sa1019" + "honnef.co/go/tools/staticcheck/sa1020" + "honnef.co/go/tools/staticcheck/sa1021" + "honnef.co/go/tools/staticcheck/sa1023" + "honnef.co/go/tools/staticcheck/sa1024" + "honnef.co/go/tools/staticcheck/sa1025" + "honnef.co/go/tools/staticcheck/sa1026" + "honnef.co/go/tools/staticcheck/sa1027" + "honnef.co/go/tools/staticcheck/sa1028" + "honnef.co/go/tools/staticcheck/sa1029" + "honnef.co/go/tools/staticcheck/sa1030" + "honnef.co/go/tools/staticcheck/sa1031" + "honnef.co/go/tools/staticcheck/sa1032" + "honnef.co/go/tools/staticcheck/sa2000" + "honnef.co/go/tools/staticcheck/sa2001" + "honnef.co/go/tools/staticcheck/sa2002" + "honnef.co/go/tools/staticcheck/sa2003" + "honnef.co/go/tools/staticcheck/sa3000" + "honnef.co/go/tools/staticcheck/sa3001" + "honnef.co/go/tools/staticcheck/sa4000" + "honnef.co/go/tools/staticcheck/sa4001" + "honnef.co/go/tools/staticcheck/sa4003" + "honnef.co/go/tools/staticcheck/sa4004" + "honnef.co/go/tools/staticcheck/sa4005" + "honnef.co/go/tools/staticcheck/sa4006" + "honnef.co/go/tools/staticcheck/sa4008" + "honnef.co/go/tools/staticcheck/sa4009" + "honnef.co/go/tools/staticcheck/sa4010" + "honnef.co/go/tools/staticcheck/sa4011" + "honnef.co/go/tools/staticcheck/sa4012" + "honnef.co/go/tools/staticcheck/sa4013" + "honnef.co/go/tools/staticcheck/sa4014" + "honnef.co/go/tools/staticcheck/sa4015" + "honnef.co/go/tools/staticcheck/sa4016" + "honnef.co/go/tools/staticcheck/sa4017" + "honnef.co/go/tools/staticcheck/sa4018" + "honnef.co/go/tools/staticcheck/sa4019" + "honnef.co/go/tools/staticcheck/sa4020" + "honnef.co/go/tools/staticcheck/sa4021" + "honnef.co/go/tools/staticcheck/sa4022" + "honnef.co/go/tools/staticcheck/sa4023" + "honnef.co/go/tools/staticcheck/sa4024" + "honnef.co/go/tools/staticcheck/sa4025" + "honnef.co/go/tools/staticcheck/sa4026" + "honnef.co/go/tools/staticcheck/sa4027" + "honnef.co/go/tools/staticcheck/sa4028" + "honnef.co/go/tools/staticcheck/sa4029" + "honnef.co/go/tools/staticcheck/sa4030" + "honnef.co/go/tools/staticcheck/sa4031" + "honnef.co/go/tools/staticcheck/sa4032" + "honnef.co/go/tools/staticcheck/sa5000" + "honnef.co/go/tools/staticcheck/sa5001" + "honnef.co/go/tools/staticcheck/sa5002" + "honnef.co/go/tools/staticcheck/sa5003" + "honnef.co/go/tools/staticcheck/sa5004" + "honnef.co/go/tools/staticcheck/sa5005" + "honnef.co/go/tools/staticcheck/sa5007" + "honnef.co/go/tools/staticcheck/sa5008" + "honnef.co/go/tools/staticcheck/sa5009" + "honnef.co/go/tools/staticcheck/sa5010" + "honnef.co/go/tools/staticcheck/sa5011" + "honnef.co/go/tools/staticcheck/sa5012" + "honnef.co/go/tools/staticcheck/sa6000" + "honnef.co/go/tools/staticcheck/sa6001" + "honnef.co/go/tools/staticcheck/sa6002" + "honnef.co/go/tools/staticcheck/sa6003" + "honnef.co/go/tools/staticcheck/sa6005" + "honnef.co/go/tools/staticcheck/sa6006" + "honnef.co/go/tools/staticcheck/sa9001" + "honnef.co/go/tools/staticcheck/sa9002" + "honnef.co/go/tools/staticcheck/sa9003" + "honnef.co/go/tools/staticcheck/sa9004" + "honnef.co/go/tools/staticcheck/sa9005" + "honnef.co/go/tools/staticcheck/sa9006" + "honnef.co/go/tools/staticcheck/sa9007" + "honnef.co/go/tools/staticcheck/sa9008" + "honnef.co/go/tools/staticcheck/sa9009" ) -func makeCallCheckerAnalyzer(rules map[string]CallCheck, extraReqs ...*analysis.Analyzer) *analysis.Analyzer { - reqs := []*analysis.Analyzer{buildir.Analyzer, tokenfile.Analyzer} - reqs = append(reqs, extraReqs...) - return &analysis.Analyzer{ - Run: callChecker(rules), - Requires: reqs, - } +var Analyzers = []*lint.Analyzer{ + sa1000.SCAnalyzer, + sa1001.SCAnalyzer, + sa1002.SCAnalyzer, + sa1003.SCAnalyzer, + sa1004.SCAnalyzer, + sa1005.SCAnalyzer, + sa1006.SCAnalyzer, + sa1007.SCAnalyzer, + sa1008.SCAnalyzer, + sa1010.SCAnalyzer, + sa1011.SCAnalyzer, + sa1012.SCAnalyzer, + sa1013.SCAnalyzer, + sa1014.SCAnalyzer, + sa1015.SCAnalyzer, + sa1016.SCAnalyzer, + sa1017.SCAnalyzer, + sa1018.SCAnalyzer, + sa1019.SCAnalyzer, + sa1020.SCAnalyzer, + sa1021.SCAnalyzer, + sa1023.SCAnalyzer, + sa1024.SCAnalyzer, + sa1025.SCAnalyzer, + sa1026.SCAnalyzer, + sa1027.SCAnalyzer, + sa1028.SCAnalyzer, + sa1029.SCAnalyzer, + sa1030.SCAnalyzer, + sa1031.SCAnalyzer, + sa1032.SCAnalyzer, + sa2000.SCAnalyzer, + sa2001.SCAnalyzer, + sa2002.SCAnalyzer, + sa2003.SCAnalyzer, + sa3000.SCAnalyzer, + sa3001.SCAnalyzer, + sa4000.SCAnalyzer, + sa4001.SCAnalyzer, + sa4003.SCAnalyzer, + sa4004.SCAnalyzer, + sa4005.SCAnalyzer, + sa4006.SCAnalyzer, + sa4008.SCAnalyzer, + sa4009.SCAnalyzer, + sa4010.SCAnalyzer, + sa4011.SCAnalyzer, + sa4012.SCAnalyzer, + sa4013.SCAnalyzer, + sa4014.SCAnalyzer, + sa4015.SCAnalyzer, + sa4016.SCAnalyzer, + sa4017.SCAnalyzer, + sa4018.SCAnalyzer, + sa4019.SCAnalyzer, + sa4020.SCAnalyzer, + sa4021.SCAnalyzer, + sa4022.SCAnalyzer, + sa4023.SCAnalyzer, + sa4024.SCAnalyzer, + sa4025.SCAnalyzer, + sa4026.SCAnalyzer, + sa4027.SCAnalyzer, + sa4028.SCAnalyzer, + sa4029.SCAnalyzer, + sa4030.SCAnalyzer, + sa4031.SCAnalyzer, + sa4032.SCAnalyzer, + sa5000.SCAnalyzer, + sa5001.SCAnalyzer, + sa5002.SCAnalyzer, + sa5003.SCAnalyzer, + sa5004.SCAnalyzer, + sa5005.SCAnalyzer, + sa5007.SCAnalyzer, + sa5008.SCAnalyzer, + sa5009.SCAnalyzer, + sa5010.SCAnalyzer, + sa5011.SCAnalyzer, + sa5012.SCAnalyzer, + sa6000.SCAnalyzer, + sa6001.SCAnalyzer, + sa6002.SCAnalyzer, + sa6003.SCAnalyzer, + sa6005.SCAnalyzer, + sa6006.SCAnalyzer, + sa9001.SCAnalyzer, + sa9002.SCAnalyzer, + sa9003.SCAnalyzer, + sa9004.SCAnalyzer, + sa9005.SCAnalyzer, + sa9006.SCAnalyzer, + sa9007.SCAnalyzer, + sa9008.SCAnalyzer, + sa9009.SCAnalyzer, } - -var Analyzers = lint.InitializeAnalyzers(Docs, map[string]*analysis.Analyzer{ - "SA1000": makeCallCheckerAnalyzer(checkRegexpRules), - "SA1001": { - Run: CheckTemplate, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - "SA1002": makeCallCheckerAnalyzer(checkTimeParseRules), - "SA1003": makeCallCheckerAnalyzer(checkEncodingBinaryRules), - "SA1004": { - Run: CheckTimeSleepConstant, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - "SA1005": { - Run: CheckExec, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - "SA1006": { - Run: CheckUnsafePrintf, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - "SA1007": makeCallCheckerAnalyzer(checkURLsRules), - "SA1008": { - Run: CheckCanonicalHeaderKey, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - "SA1010": makeCallCheckerAnalyzer(checkRegexpFindAllRules), - "SA1011": makeCallCheckerAnalyzer(checkUTF8CutsetRules), - "SA1012": { - Run: CheckNilContext, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - "SA1013": { - Run: CheckSeeker, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - "SA1014": makeCallCheckerAnalyzer(checkUnmarshalPointerRules), - "SA1015": { - Run: CheckLeakyTimeTick, - Requires: []*analysis.Analyzer{buildir.Analyzer}, - }, - "SA1016": { - Run: CheckUntrappableSignal, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - "SA1017": makeCallCheckerAnalyzer(checkUnbufferedSignalChanRules), - "SA1018": makeCallCheckerAnalyzer(checkStringsReplaceZeroRules), - "SA1019": { - Run: CheckDeprecated, - Requires: []*analysis.Analyzer{inspect.Analyzer, deprecated.Analyzer, generated.Analyzer}, - }, - "SA1020": makeCallCheckerAnalyzer(checkListenAddressRules), - "SA1021": makeCallCheckerAnalyzer(checkBytesEqualIPRules), - "SA1023": { - Run: CheckWriterBufferModified, - Requires: []*analysis.Analyzer{buildir.Analyzer}, - }, - "SA1024": makeCallCheckerAnalyzer(checkUniqueCutsetRules), - "SA1025": { - Run: CheckTimerResetReturnValue, - Requires: []*analysis.Analyzer{buildir.Analyzer}, - }, - "SA1026": makeCallCheckerAnalyzer(checkUnsupportedMarshal), - "SA1027": makeCallCheckerAnalyzer(checkAtomicAlignment), - "SA1028": makeCallCheckerAnalyzer(checkSortSliceRules), - "SA1029": makeCallCheckerAnalyzer(checkWithValueKeyRules), - "SA1030": makeCallCheckerAnalyzer(checkStrconvRules), - - "SA2000": { - Run: CheckWaitgroupAdd, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - "SA2001": { - Run: CheckEmptyCriticalSection, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - "SA2002": { - Run: CheckConcurrentTesting, - Requires: []*analysis.Analyzer{buildir.Analyzer}, - }, - "SA2003": { - Run: CheckDeferLock, - Requires: []*analysis.Analyzer{buildir.Analyzer}, - }, - - "SA3000": { - Run: CheckTestMainExit, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - "SA3001": { - Run: CheckBenchmarkN, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - - "SA4000": { - Run: CheckLhsRhsIdentical, - Requires: []*analysis.Analyzer{inspect.Analyzer, tokenfile.Analyzer, generated.Analyzer}, - }, - "SA4001": { - Run: CheckIneffectiveCopy, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - "SA4003": { - Run: CheckExtremeComparison, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - "SA4004": { - Run: CheckIneffectiveLoop, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - "SA4005": { - Run: CheckIneffectiveFieldAssignments, - Requires: []*analysis.Analyzer{buildir.Analyzer}, - }, - "SA4006": { - Run: CheckUnreadVariableValues, - Requires: []*analysis.Analyzer{buildir.Analyzer, generated.Analyzer}, - }, - "SA4008": { - Run: CheckLoopCondition, - Requires: []*analysis.Analyzer{buildir.Analyzer}, - }, - "SA4009": { - Run: CheckArgOverwritten, - Requires: []*analysis.Analyzer{buildir.Analyzer}, - }, - "SA4010": { - Run: CheckIneffectiveAppend, - Requires: []*analysis.Analyzer{buildir.Analyzer}, - }, - "SA4011": { - Run: CheckScopedBreak, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - "SA4012": { - Run: CheckNaNComparison, - Requires: []*analysis.Analyzer{buildir.Analyzer}, - }, - "SA4013": { - Run: CheckDoubleNegation, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - "SA4014": { - Run: CheckRepeatedIfElse, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - "SA4015": makeCallCheckerAnalyzer(checkMathIntRules), - "SA4016": { - Run: CheckSillyBitwiseOps, - Requires: []*analysis.Analyzer{inspect.Analyzer, tokenfile.Analyzer}, - }, - "SA4017": { - Run: CheckSideEffectFreeCalls, - Requires: []*analysis.Analyzer{buildir.Analyzer, purity.Analyzer}, - }, - "SA4018": { - Run: CheckSelfAssignment, - Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer, tokenfile.Analyzer, purity.Analyzer}, - }, - "SA4019": { - Run: CheckDuplicateBuildConstraints, - Requires: []*analysis.Analyzer{generated.Analyzer}, - }, - "SA4020": { - Run: CheckUnreachableTypeCases, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - "SA4021": { - Run: CheckSingleArgAppend, - Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer, tokenfile.Analyzer}, - }, - "SA4022": { - Run: CheckAddressIsNil, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - "SA4023": { - Run: CheckTypedNilInterface, - Requires: []*analysis.Analyzer{buildir.Analyzer, typedness.Analysis, nilness.Analysis}, - }, - "SA4024": { - Run: CheckBuiltinZeroComparison, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - "SA4025": { - Run: CheckIntegerDivisionEqualsZero, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - "SA4026": { - Run: CheckNegativeZeroFloat, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - "SA4027": { - Run: CheckIneffectiveURLQueryModification, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - "SA4028": { - Run: CheckModuloOne, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - "SA4029": { - Run: CheckIneffectiveSort, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - "SA4030": { - Run: CheckIneffectiveRandInt, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - "SA4031": { - Run: CheckAllocationNilCheck, - Requires: []*analysis.Analyzer{buildir.Analyzer, inspect.Analyzer, tokenfile.Analyzer}, - }, - - "SA5000": { - Run: CheckNilMaps, - Requires: []*analysis.Analyzer{buildir.Analyzer}, - }, - "SA5001": { - Run: CheckEarlyDefer, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - "SA5002": { - Run: CheckInfiniteEmptyLoop, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - "SA5003": { - Run: CheckDeferInInfiniteLoop, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - "SA5004": { - Run: CheckLoopEmptyDefault, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - "SA5005": { - Run: CheckCyclicFinalizer, - Requires: []*analysis.Analyzer{buildir.Analyzer}, - }, - "SA5007": { - Run: CheckInfiniteRecursion, - Requires: []*analysis.Analyzer{buildir.Analyzer}, - }, - "SA5008": { - Run: CheckStructTags, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - "SA5009": makeCallCheckerAnalyzer(checkPrintfRules), - "SA5010": { - Run: CheckImpossibleTypeAssertion, - Requires: []*analysis.Analyzer{buildir.Analyzer, tokenfile.Analyzer}, - }, - "SA5011": { - Run: CheckMaybeNil, - Requires: []*analysis.Analyzer{buildir.Analyzer}, - }, - "SA5012": { - Run: CheckEvenSliceLength, - FactTypes: []analysis.Fact{new(evenElements)}, - Requires: []*analysis.Analyzer{buildir.Analyzer}, - }, - - "SA6000": makeCallCheckerAnalyzer(checkRegexpMatchLoopRules), - "SA6001": { - Run: CheckMapBytesKey, - Requires: []*analysis.Analyzer{buildir.Analyzer}, - }, - "SA6002": makeCallCheckerAnalyzer(checkSyncPoolValueRules), - "SA6003": { - Run: CheckRangeStringRunes, - Requires: []*analysis.Analyzer{buildir.Analyzer}, - }, - "SA6005": { - Run: CheckToLowerToUpperComparison, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - - "SA9001": { - Run: CheckDubiousDeferInChannelRangeLoop, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - "SA9002": { - Run: CheckNonOctalFileMode, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - "SA9003": { - Run: CheckEmptyBranch, - Requires: []*analysis.Analyzer{buildir.Analyzer, tokenfile.Analyzer, generated.Analyzer}, - }, - "SA9004": { - Run: CheckMissingEnumTypesInDeclaration, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - // Filtering generated code because it may include empty structs generated from data models. - "SA9005": makeCallCheckerAnalyzer(checkNoopMarshal, generated.Analyzer), - "SA9006": { - Run: CheckStaticBitShift, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - "SA9007": { - Run: CheckBadRemoveAll, - Requires: []*analysis.Analyzer{buildir.Analyzer}, - }, - - "SA9008": { - Run: CheckTypeAssertionShadowingElse, - Requires: []*analysis.Analyzer{inspect.Analyzer, buildir.Analyzer, tokenfile.Analyzer}, - }, -}) diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/buildtag.go b/tools/vendor/honnef.co/go/tools/staticcheck/buildtag.go deleted file mode 100644 index 97ccf77b4b..0000000000 --- a/tools/vendor/honnef.co/go/tools/staticcheck/buildtag.go +++ /dev/null @@ -1,21 +0,0 @@ -package staticcheck - -import ( - "go/ast" - "strings" - - "honnef.co/go/tools/go/ast/astutil" -) - -func buildTags(f *ast.File) [][]string { - var out [][]string - for _, line := range strings.Split(astutil.Preamble(f), "\n") { - if !strings.HasPrefix(line, "+build ") { - continue - } - line = strings.TrimSpace(strings.TrimPrefix(line, "+build ")) - fields := strings.Fields(line) - out = append(out, fields) - } - return out -} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/doc.go b/tools/vendor/honnef.co/go/tools/staticcheck/doc.go index c28fdbf04b..462acee3df 100644 --- a/tools/vendor/honnef.co/go/tools/staticcheck/doc.go +++ b/tools/vendor/honnef.co/go/tools/staticcheck/doc.go @@ -1,1302 +1,5 @@ +//go:generate go run ../generate.go + // Package staticcheck contains analyzes that find bugs and performance issues. // Barring the rare false positive, any code flagged by these analyzes needs to be fixed. package staticcheck - -import "honnef.co/go/tools/analysis/lint" - -var Docs = lint.Markdownify(map[string]*lint.RawDocumentation{ - "SA1000": { - Title: `Invalid regular expression`, - Since: "2017.1", - Severity: lint.SeverityError, - MergeIf: lint.MergeIfAny, - }, - - "SA1001": { - Title: `Invalid template`, - Since: "2017.1", - Severity: lint.SeverityError, - MergeIf: lint.MergeIfAny, - }, - - "SA1002": { - Title: `Invalid format in \'time.Parse\'`, - Since: "2017.1", - Severity: lint.SeverityError, - MergeIf: lint.MergeIfAny, - }, - - "SA1003": { - Title: `Unsupported argument to functions in \'encoding/binary\'`, - Text: `The \'encoding/binary\' package can only serialize types with known sizes. -This precludes the use of the \'int\' and \'uint\' types, as their sizes -differ on different architectures. Furthermore, it doesn't support -serializing maps, channels, strings, or functions. - -Before Go 1.8, \'bool\' wasn't supported, either.`, - Since: "2017.1", - Severity: lint.SeverityError, - MergeIf: lint.MergeIfAny, - }, - - "SA1004": { - Title: `Suspiciously small untyped constant in \'time.Sleep\'`, - Text: `The \'time\'.Sleep function takes a \'time.Duration\' as its only argument. -Durations are expressed in nanoseconds. Thus, calling \'time.Sleep(1)\' -will sleep for 1 nanosecond. This is a common source of bugs, as sleep -functions in other languages often accept seconds or milliseconds. - -The \'time\' package provides constants such as \'time.Second\' to express -large durations. These can be combined with arithmetic to express -arbitrary durations, for example \'5 * time.Second\' for 5 seconds. - -If you truly meant to sleep for a tiny amount of time, use -\'n * time.Nanosecond\' to signal to Staticcheck that you did mean to sleep -for some amount of nanoseconds.`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA1005": { - Title: `Invalid first argument to \'exec.Command\'`, - Text: `\'os/exec\' runs programs directly (using variants of the fork and exec -system calls on Unix systems). This shouldn't be confused with running -a command in a shell. The shell will allow for features such as input -redirection, pipes, and general scripting. The shell is also -responsible for splitting the user's input into a program name and its -arguments. For example, the equivalent to - - ls / /tmp - -would be - - exec.Command("ls", "/", "/tmp") - -If you want to run a command in a shell, consider using something like -the following – but be aware that not all systems, particularly -Windows, will have a \'/bin/sh\' program: - - exec.Command("/bin/sh", "-c", "ls | grep Awesome")`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA1006": { - Title: `\'Printf\' with dynamic first argument and no further arguments`, - Text: `Using \'fmt.Printf\' with a dynamic first argument can lead to unexpected -output. The first argument is a format string, where certain character -combinations have special meaning. If, for example, a user were to -enter a string such as - - Interest rate: 5% - -and you printed it with - - fmt.Printf(s) - -it would lead to the following output: - - Interest rate: 5%!(NOVERB). - -Similarly, forming the first parameter via string concatenation with -user input should be avoided for the same reason. When printing user -input, either use a variant of \'fmt.Print\', or use the \'%s\' Printf verb -and pass the string as an argument.`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA1007": { - Title: `Invalid URL in \'net/url.Parse\'`, - Since: "2017.1", - Severity: lint.SeverityError, - MergeIf: lint.MergeIfAny, - }, - - "SA1008": { - Title: `Non-canonical key in \'http.Header\' map`, - Text: `Keys in \'http.Header\' maps are canonical, meaning they follow a specific -combination of uppercase and lowercase letters. Methods such as -\'http.Header.Add\' and \'http.Header.Del\' convert inputs into this canonical -form before manipulating the map. - -When manipulating \'http.Header\' maps directly, as opposed to using the -provided methods, care should be taken to stick to canonical form in -order to avoid inconsistencies. The following piece of code -demonstrates one such inconsistency: - - h := http.Header{} - h["etag"] = []string{"1234"} - h.Add("etag", "5678") - fmt.Println(h) - - // Output: - // map[Etag:[5678] etag:[1234]] - -The easiest way of obtaining the canonical form of a key is to use -\'http.CanonicalHeaderKey\'.`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA1010": { - Title: `\'(*regexp.Regexp).FindAll\' called with \'n == 0\', which will always return zero results`, - Text: `If \'n >= 0\', the function returns at most \'n\' matches/submatches. To -return all results, specify a negative number.`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, // MergeIfAny if we only flag literals, not named constants - }, - - "SA1011": { - Title: `Various methods in the \"strings\" package expect valid UTF-8, but invalid input is provided`, - Since: "2017.1", - Severity: lint.SeverityError, - MergeIf: lint.MergeIfAny, - }, - - "SA1012": { - Title: `A nil \'context.Context\' is being passed to a function, consider using \'context.TODO\' instead`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA1013": { - Title: `\'io.Seeker.Seek\' is being called with the whence constant as the first argument, but it should be the second`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA1014": { - Title: `Non-pointer value passed to \'Unmarshal\' or \'Decode\'`, - Since: "2017.1", - Severity: lint.SeverityError, - MergeIf: lint.MergeIfAny, - }, - - "SA1015": { - Title: `Using \'time.Tick\' in a way that will leak. Consider using \'time.NewTicker\', and only use \'time.Tick\' in tests, commands and endless functions`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA1016": { - Title: `Trapping a signal that cannot be trapped`, - Text: `Not all signals can be intercepted by a process. Specifically, on -UNIX-like systems, the \'syscall.SIGKILL\' and \'syscall.SIGSTOP\' signals are -never passed to the process, but instead handled directly by the -kernel. It is therefore pointless to try and handle these signals.`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA1017": { - Title: `Channels used with \'os/signal.Notify\' should be buffered`, - Text: `The \'os/signal\' package uses non-blocking channel sends when delivering -signals. If the receiving end of the channel isn't ready and the -channel is either unbuffered or full, the signal will be dropped. To -avoid missing signals, the channel should be buffered and of the -appropriate size. For a channel used for notification of just one -signal value, a buffer of size 1 is sufficient.`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA1018": { - Title: `\'strings.Replace\' called with \'n == 0\', which does nothing`, - Text: `With \'n == 0\', zero instances will be replaced. To replace all -instances, use a negative number, or use \'strings.ReplaceAll\'.`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, // MergeIfAny if we only flag literals, not named constants - }, - - "SA1019": { - Title: `Using a deprecated function, variable, constant or field`, - Since: "2017.1", - Severity: lint.SeverityDeprecated, - MergeIf: lint.MergeIfAny, - }, - - "SA1020": { - Title: `Using an invalid host:port pair with a \'net.Listen\'-related function`, - Since: "2017.1", - Severity: lint.SeverityError, - MergeIf: lint.MergeIfAny, - }, - - "SA1021": { - Title: `Using \'bytes.Equal\' to compare two \'net.IP\'`, - Text: `A \'net.IP\' stores an IPv4 or IPv6 address as a slice of bytes. The -length of the slice for an IPv4 address, however, can be either 4 or -16 bytes long, using different ways of representing IPv4 addresses. In -order to correctly compare two \'net.IP\'s, the \'net.IP.Equal\' method should -be used, as it takes both representations into account.`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA1023": { - Title: `Modifying the buffer in an \'io.Writer\' implementation`, - Text: `\'Write\' must not modify the slice data, even temporarily.`, - Since: "2017.1", - Severity: lint.SeverityError, - MergeIf: lint.MergeIfAny, - }, - - "SA1024": { - Title: `A string cutset contains duplicate characters`, - Text: `The \'strings.TrimLeft\' and \'strings.TrimRight\' functions take cutsets, not -prefixes. A cutset is treated as a set of characters to remove from a -string. For example, - - strings.TrimLeft("42133word", "1234") - -will result in the string \'"word"\' – any characters that are 1, 2, 3 or -4 are cut from the left of the string. - -In order to remove one string from another, use \'strings.TrimPrefix\' instead.`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA1025": { - Title: `It is not possible to use \'(*time.Timer).Reset\''s return value correctly`, - Since: "2019.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA1026": { - Title: `Cannot marshal channels or functions`, - Since: "2019.2", - Severity: lint.SeverityError, - MergeIf: lint.MergeIfAny, - }, - - "SA1027": { - Title: `Atomic access to 64-bit variable must be 64-bit aligned`, - Text: `On ARM, x86-32, and 32-bit MIPS, it is the caller's responsibility to -arrange for 64-bit alignment of 64-bit words accessed atomically. The -first word in a variable or in an allocated struct, array, or slice -can be relied upon to be 64-bit aligned. - -You can use the structlayout tool to inspect the alignment of fields -in a struct.`, - Since: "2019.2", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA1028": { - Title: `\'sort.Slice\' can only be used on slices`, - Text: `The first argument of \'sort.Slice\' must be a slice.`, - Since: "2020.1", - Severity: lint.SeverityError, - MergeIf: lint.MergeIfAny, - }, - - "SA1029": { - Title: `Inappropriate key in call to \'context.WithValue\'`, - Text: `The provided key must be comparable and should not be -of type \'string\' or any other built-in type to avoid collisions between -packages using context. Users of \'WithValue\' should define their own -types for keys. - -To avoid allocating when assigning to an \'interface{}\', -context keys often have concrete type \'struct{}\'. Alternatively, -exported context key variables' static type should be a pointer or -interface.`, - Since: "2020.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA1030": { - Title: `Invalid argument in call to a \'strconv\' function`, - Text: `This check validates the format, number base and bit size arguments of -the various parsing and formatting functions in \'strconv\'.`, - Since: "2021.1", - Severity: lint.SeverityError, - MergeIf: lint.MergeIfAny, - }, - - "SA2000": { - Title: `\'sync.WaitGroup.Add\' called inside the goroutine, leading to a race condition`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA2001": { - Title: `Empty critical section, did you mean to defer the unlock?`, - Text: `Empty critical sections of the kind - - mu.Lock() - mu.Unlock() - -are very often a typo, and the following was intended instead: - - mu.Lock() - defer mu.Unlock() - -Do note that sometimes empty critical sections can be useful, as a -form of signaling to wait on another goroutine. Many times, there are -simpler ways of achieving the same effect. When that isn't the case, -the code should be amply commented to avoid confusion. Combining such -comments with a \'//lint:ignore\' directive can be used to suppress this -rare false positive.`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA2002": { - Title: `Called \'testing.T.FailNow\' or \'SkipNow\' in a goroutine, which isn't allowed`, - Since: "2017.1", - Severity: lint.SeverityError, - MergeIf: lint.MergeIfAny, - }, - - "SA2003": { - Title: `Deferred \'Lock\' right after locking, likely meant to defer \'Unlock\' instead`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA3000": { - Title: `\'TestMain\' doesn't call \'os.Exit\', hiding test failures`, - Text: `Test executables (and in turn \"go test\") exit with a non-zero status -code if any tests failed. When specifying your own \'TestMain\' function, -it is your responsibility to arrange for this, by calling \'os.Exit\' with -the correct code. The correct code is returned by \'(*testing.M).Run\', so -the usual way of implementing \'TestMain\' is to end it with -\'os.Exit(m.Run())\'.`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA3001": { - Title: `Assigning to \'b.N\' in benchmarks distorts the results`, - Text: `The testing package dynamically sets \'b.N\' to improve the reliability of -benchmarks and uses it in computations to determine the duration of a -single operation. Benchmark code must not alter \'b.N\' as this would -falsify results.`, - Since: "2017.1", - Severity: lint.SeverityError, - MergeIf: lint.MergeIfAny, - }, - - "SA4000": { - Title: `Binary operator has identical expressions on both sides`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA4001": { - Title: `\'&*x\' gets simplified to \'x\', it does not copy \'x\'`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA4003": { - Title: `Comparing unsigned values against negative values is pointless`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAll, - }, - - "SA4004": { - Title: `The loop exits unconditionally after one iteration`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAll, - }, - - "SA4005": { - Title: `Field assignment that will never be observed. Did you mean to use a pointer receiver?`, - Since: "2021.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA4006": { - Title: `A value assigned to a variable is never read before being overwritten. Forgotten error check or dead code?`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAll, - }, - - "SA4008": { - Title: `The variable in the loop condition never changes, are you incrementing the wrong variable?`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAll, - }, - - "SA4009": { - Title: `A function argument is overwritten before its first use`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA4010": { - Title: `The result of \'append\' will never be observed anywhere`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAll, - }, - - "SA4011": { - Title: `Break statement with no effect. Did you mean to break out of an outer loop?`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA4012": { - Title: `Comparing a value against NaN even though no value is equal to NaN`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA4013": { - Title: `Negating a boolean twice (\'!!b\') is the same as writing \'b\'. This is either redundant, or a typo.`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA4014": { - Title: `An if/else if chain has repeated conditions and no side-effects; if the condition didn't match the first time, it won't match the second time, either`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAll, - }, - - "SA4015": { - Title: `Calling functions like \'math.Ceil\' on floats converted from integers doesn't do anything useful`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAll, - }, - - "SA4016": { - Title: `Certain bitwise operations, such as \'x ^ 0\', do not do anything useful`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, // MergeIfAny if we only flag literals, not named constants - }, - - "SA4017": { - Title: `Discarding the return values of a function without side effects, making the call pointless`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAll, - }, - - "SA4018": { - Title: `Self-assignment of variables`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA4019": { - Title: `Multiple, identical build constraints in the same file`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA4020": { - Title: `Unreachable case clause in a type switch`, - Text: `In a type switch like the following - - type T struct{} - func (T) Read(b []byte) (int, error) { return 0, nil } - - var v interface{} = T{} - - switch v.(type) { - case io.Reader: - // ... - case T: - // unreachable - } - -the second case clause can never be reached because \'T\' implements -\'io.Reader\' and case clauses are evaluated in source order. - -Another example: - - type T struct{} - func (T) Read(b []byte) (int, error) { return 0, nil } - func (T) Close() error { return nil } - - var v interface{} = T{} - - switch v.(type) { - case io.Reader: - // ... - case io.ReadCloser: - // unreachable - } - -Even though \'T\' has a \'Close\' method and thus implements \'io.ReadCloser\', -\'io.Reader\' will always match first. The method set of \'io.Reader\' is a -subset of \'io.ReadCloser\'. Thus it is impossible to match the second -case without matching the first case. - - -Structurally equivalent interfaces - -A special case of the previous example are structurally identical -interfaces. Given these declarations - - type T error - type V error - - func doSomething() error { - err, ok := doAnotherThing() - if ok { - return T(err) - } - - return U(err) - } - -the following type switch will have an unreachable case clause: - - switch doSomething().(type) { - case T: - // ... - case V: - // unreachable - } - -\'T\' will always match before V because they are structurally equivalent -and therefore \'doSomething()\''s return value implements both.`, - Since: "2019.2", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAll, - }, - - "SA4021": { - Title: `\"x = append(y)\" is equivalent to \"x = y\"`, - Since: "2019.2", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA4022": { - Title: `Comparing the address of a variable against nil`, - Text: `Code such as \"if &x == nil\" is meaningless, because taking the address of a variable always yields a non-nil pointer.`, - Since: "2020.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA4023": { - Title: `Impossible comparison of interface value with untyped nil`, - Text: `Under the covers, interfaces are implemented as two elements, a -type T and a value V. V is a concrete value such as an int, -struct or pointer, never an interface itself, and has type T. For -instance, if we store the int value 3 in an interface, the -resulting interface value has, schematically, (T=int, V=3). The -value V is also known as the interface's dynamic value, since a -given interface variable might hold different values V (and -corresponding types T) during the execution of the program. - -An interface value is nil only if the V and T are both -unset, (T=nil, V is not set), In particular, a nil interface will -always hold a nil type. If we store a nil pointer of type *int -inside an interface value, the inner type will be *int regardless -of the value of the pointer: (T=*int, V=nil). Such an interface -value will therefore be non-nil even when the pointer value V -inside is nil. - -This situation can be confusing, and arises when a nil value is -stored inside an interface value such as an error return: - - func returnsError() error { - var p *MyError = nil - if bad() { - p = ErrBad - } - return p // Will always return a non-nil error. - } - -If all goes well, the function returns a nil p, so the return -value is an error interface value holding (T=*MyError, V=nil). -This means that if the caller compares the returned error to nil, -it will always look as if there was an error even if nothing bad -happened. To return a proper nil error to the caller, the -function must return an explicit nil: - - func returnsError() error { - if bad() { - return ErrBad - } - return nil - } - -It's a good idea for functions that return errors always to use -the error type in their signature (as we did above) rather than a -concrete type such as \'*MyError\', to help guarantee the error is -created correctly. As an example, \'os.Open\' returns an error even -though, if not nil, it's always of concrete type *os.PathError. - -Similar situations to those described here can arise whenever -interfaces are used. Just keep in mind that if any concrete value -has been stored in the interface, the interface will not be nil. -For more information, see The Laws of -Reflection (https://golang.org/doc/articles/laws_of_reflection.html). - -This text has been copied from -https://golang.org/doc/faq#nil_error, licensed under the Creative -Commons Attribution 3.0 License.`, - Since: "2020.2", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, // TODO should this be MergeIfAll? - }, - - "SA4024": { - Title: `Checking for impossible return value from a builtin function`, - Text: `Return values of the \'len\' and \'cap\' builtins cannot be negative. - -See https://golang.org/pkg/builtin/#len and https://golang.org/pkg/builtin/#cap. - -Example: - - if len(slice) < 0 { - fmt.Println("unreachable code") - }`, - Since: "2021.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA4025": { - Title: "Integer division of literals that results in zero", - Text: `When dividing two integer constants, the result will -also be an integer. Thus, a division such as \'2 / 3\' results in \'0\'. -This is true for all of the following examples: - - _ = 2 / 3 - const _ = 2 / 3 - const _ float64 = 2 / 3 - _ = float64(2 / 3) - -Staticcheck will flag such divisions if both sides of the division are -integer literals, as it is highly unlikely that the division was -intended to truncate to zero. Staticcheck will not flag integer -division involving named constants, to avoid noisy positives. -`, - Since: "2021.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA4026": { - Title: "Go constants cannot express negative zero", - Text: `In IEEE 754 floating point math, zero has a sign and can be positive -or negative. This can be useful in certain numerical code. - -Go constants, however, cannot express negative zero. This means that -the literals \'-0.0\' and \'0.0\' have the same ideal value (zero) and -will both represent positive zero at runtime. - -To explicitly and reliably create a negative zero, you can use the -\'math.Copysign\' function: \'math.Copysign(0, -1)\'.`, - Since: "2021.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA4027": { - Title: `\'(*net/url.URL).Query\' returns a copy, modifying it doesn't change the URL`, - Text: `\'(*net/url.URL).Query\' parses the current value of \'net/url.URL.RawQuery\' -and returns it as a map of type \'net/url.Values\'. Subsequent changes to -this map will not affect the URL unless the map gets encoded and -assigned to the URL's \'RawQuery\'. - -As a consequence, the following code pattern is an expensive no-op: -\'u.Query().Add(key, value)\'.`, - Since: "2021.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA4028": { - Title: `\'x % 1\' is always zero`, - Since: "2022.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, // MergeIfAny if we only flag literals, not named constants - }, - - "SA4029": { - Title: "Ineffective attempt at sorting slice", - Text: ` -\'sort.Float64Slice\', \'sort.IntSlice\', and \'sort.StringSlice\' are -types, not functions. Doing \'x = sort.StringSlice(x)\' does nothing, -especially not sort any values. The correct usage is -\'sort.Sort(sort.StringSlice(x))\' or \'sort.StringSlice(x).Sort()\', -but there are more convenient helpers, namely \'sort.Float64s\', -\'sort.Ints\', and \'sort.Strings\'. -`, - Since: "2022.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA4030": { - Title: "Ineffective attempt at generating random number", - Text: ` -Functions in the \'math/rand\' package that accept upper limits, such -as \'Intn\', generate random numbers in the half-open interval [0,n). In -other words, the generated numbers will be \'>= 0\' and \'< n\' – they -don't include \'n\'. \'rand.Intn(1)\' therefore doesn't generate \'0\' -or \'1\', it always generates \'0\'.`, - Since: "2022.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA4031": { - Title: `Checking never-nil value against nil`, - Since: "2022.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA5000": { - Title: `Assignment to nil map`, - Since: "2017.1", - Severity: lint.SeverityError, - MergeIf: lint.MergeIfAny, - }, - - "SA5001": { - Title: `Deferring \'Close\' before checking for a possible error`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA5002": { - Title: `The empty for loop (\"for {}\") spins and can block the scheduler`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA5003": { - Title: `Defers in infinite loops will never execute`, - Text: `Defers are scoped to the surrounding function, not the surrounding -block. In a function that never returns, i.e. one containing an -infinite loop, defers will never execute.`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA5004": { - Title: `\"for { select { ...\" with an empty default branch spins`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA5005": { - Title: `The finalizer references the finalized object, preventing garbage collection`, - Text: `A finalizer is a function associated with an object that runs when the -garbage collector is ready to collect said object, that is when the -object is no longer referenced by anything. - -If the finalizer references the object, however, it will always remain -as the final reference to that object, preventing the garbage -collector from collecting the object. The finalizer will never run, -and the object will never be collected, leading to a memory leak. That -is why the finalizer should instead use its first argument to operate -on the object. That way, the number of references can temporarily go -to zero before the object is being passed to the finalizer.`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA5007": { - Title: `Infinite recursive call`, - Text: `A function that calls itself recursively needs to have an exit -condition. Otherwise it will recurse forever, until the system runs -out of memory. - -This issue can be caused by simple bugs such as forgetting to add an -exit condition. It can also happen "on purpose". Some languages have -tail call optimization which makes certain infinite recursive calls -safe to use. Go, however, does not implement TCO, and as such a loop -should be used instead.`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA5008": { - Title: `Invalid struct tag`, - Since: "2019.2", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA5009": { - Title: `Invalid Printf call`, - Since: "2019.2", - Severity: lint.SeverityError, - MergeIf: lint.MergeIfAny, - }, - - "SA5010": { - Title: `Impossible type assertion`, - - Text: `Some type assertions can be statically proven to be -impossible. This is the case when the method sets of both -arguments of the type assertion conflict with each other, for -example by containing the same method with different -signatures. - -The Go compiler already applies this check when asserting from an -interface value to a concrete type. If the concrete type misses -methods from the interface, or if function signatures don't match, -then the type assertion can never succeed. - -This check applies the same logic when asserting from one interface to -another. If both interface types contain the same method but with -different signatures, then the type assertion can never succeed, -either.`, - - Since: "2020.1", - Severity: lint.SeverityWarning, - // Technically this should be MergeIfAll, but the Go compiler - // already flags some impossible type assertions, so - // MergeIfAny is consistent with the compiler. - MergeIf: lint.MergeIfAny, - }, - - "SA5011": { - Title: `Possible nil pointer dereference`, - - Text: `A pointer is being dereferenced unconditionally, while -also being checked against nil in another place. This suggests that -the pointer may be nil and dereferencing it may panic. This is -commonly a result of improperly ordered code or missing return -statements. Consider the following examples: - - func fn(x *int) { - fmt.Println(*x) - - // This nil check is equally important for the previous dereference - if x != nil { - foo(*x) - } - } - - func TestFoo(t *testing.T) { - x := compute() - if x == nil { - t.Errorf("nil pointer received") - } - - // t.Errorf does not abort the test, so if x is nil, the next line will panic. - foo(*x) - } - -Staticcheck tries to deduce which functions abort control flow. -For example, it is aware that a function will not continue -execution after a call to \'panic\' or \'log.Fatal\'. However, sometimes -this detection fails, in particular in the presence of -conditionals. Consider the following example: - - func Log(msg string, level int) { - fmt.Println(msg) - if level == levelFatal { - os.Exit(1) - } - } - - func Fatal(msg string) { - Log(msg, levelFatal) - } - - func fn(x *int) { - if x == nil { - Fatal("unexpected nil pointer") - } - fmt.Println(*x) - } - -Staticcheck will flag the dereference of \'x\', even though it is perfectly -safe. Staticcheck is not able to deduce that a call to -Fatal will exit the program. For the time being, the easiest -workaround is to modify the definition of Fatal like so: - - func Fatal(msg string) { - Log(msg, levelFatal) - panic("unreachable") - } - -We also hard-code functions from common logging packages such as -logrus. Please file an issue if we're missing support for a -popular package.`, - Since: "2020.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA5012": { - Title: "Passing odd-sized slice to function expecting even size", - Text: `Some functions that take slices as parameters expect the slices to have an even number of elements. -Often, these functions treat elements in a slice as pairs. -For example, \'strings.NewReplacer\' takes pairs of old and new strings, -and calling it with an odd number of elements would be an error.`, - Since: "2020.2", - Severity: lint.SeverityError, - MergeIf: lint.MergeIfAny, - }, - - "SA6000": { - Title: `Using \'regexp.Match\' or related in a loop, should use \'regexp.Compile\'`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA6001": { - Title: `Missing an optimization opportunity when indexing maps by byte slices`, - - Text: `Map keys must be comparable, which precludes the use of byte slices. -This usually leads to using string keys and converting byte slices to -strings. - -Normally, a conversion of a byte slice to a string needs to copy the data and -causes allocations. The compiler, however, recognizes \'m[string(b)]\' and -uses the data of \'b\' directly, without copying it, because it knows that -the data can't change during the map lookup. This leads to the -counter-intuitive situation that - - k := string(b) - println(m[k]) - println(m[k]) - -will be less efficient than - - println(m[string(b)]) - println(m[string(b)]) - -because the first version needs to copy and allocate, while the second -one does not. - -For some history on this optimization, check out commit -f5f5a8b6209f84961687d993b93ea0d397f5d5bf in the Go repository.`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA6002": { - Title: `Storing non-pointer values in \'sync.Pool\' allocates memory`, - Text: `A \'sync.Pool\' is used to avoid unnecessary allocations and reduce the -amount of work the garbage collector has to do. - -When passing a value that is not a pointer to a function that accepts -an interface, the value needs to be placed on the heap, which means an -additional allocation. Slices are a common thing to put in sync.Pools, -and they're structs with 3 fields (length, capacity, and a pointer to -an array). In order to avoid the extra allocation, one should store a -pointer to the slice instead. - -See the comments on https://go-review.googlesource.com/c/go/+/24371 -that discuss this problem.`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA6003": { - Title: `Converting a string to a slice of runes before ranging over it`, - Text: `You may want to loop over the runes in a string. Instead of converting -the string to a slice of runes and looping over that, you can loop -over the string itself. That is, - - for _, r := range s {} - -and - - for _, r := range []rune(s) {} - -will yield the same values. The first version, however, will be faster -and avoid unnecessary memory allocations. - -Do note that if you are interested in the indices, ranging over a -string and over a slice of runes will yield different indices. The -first one yields byte offsets, while the second one yields indices in -the slice of runes.`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA6005": { - Title: `Inefficient string comparison with \'strings.ToLower\' or \'strings.ToUpper\'`, - Text: `Converting two strings to the same case and comparing them like so - - if strings.ToLower(s1) == strings.ToLower(s2) { - ... - } - -is significantly more expensive than comparing them with -\'strings.EqualFold(s1, s2)\'. This is due to memory usage as well as -computational complexity. - -\'strings.ToLower\' will have to allocate memory for the new strings, as -well as convert both strings fully, even if they differ on the very -first byte. strings.EqualFold, on the other hand, compares the strings -one character at a time. It doesn't need to create two intermediate -strings and can return as soon as the first non-matching character has -been found. - -For a more in-depth explanation of this issue, see -https://blog.digitalocean.com/how-to-efficiently-compare-strings-in-go/`, - Since: "2019.2", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA9001": { - Title: `Defers in range loops may not run when you expect them to`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA9002": { - Title: `Using a non-octal \'os.FileMode\' that looks like it was meant to be in octal.`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA9003": { - Title: `Empty body in an if or else branch`, - Since: "2017.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA9004": { - Title: `Only the first constant has an explicit type`, - - Text: `In a constant declaration such as the following: - - const ( - First byte = 1 - Second = 2 - ) - -the constant Second does not have the same type as the constant First. -This construct shouldn't be confused with - - const ( - First byte = iota - Second - ) - -where \'First\' and \'Second\' do indeed have the same type. The type is only -passed on when no explicit value is assigned to the constant. - -When declaring enumerations with explicit values it is therefore -important not to write - - const ( - EnumFirst EnumType = 1 - EnumSecond = 2 - EnumThird = 3 - ) - -This discrepancy in types can cause various confusing behaviors and -bugs. - - -Wrong type in variable declarations - -The most obvious issue with such incorrect enumerations expresses -itself as a compile error: - - package pkg - - const ( - EnumFirst uint8 = 1 - EnumSecond = 2 - ) - - func fn(useFirst bool) { - x := EnumSecond - if useFirst { - x = EnumFirst - } - } - -fails to compile with - - ./const.go:11:5: cannot use EnumFirst (type uint8) as type int in assignment - - -Losing method sets - -A more subtle issue occurs with types that have methods and optional -interfaces. Consider the following: - - package main - - import "fmt" - - type Enum int - - func (e Enum) String() string { - return "an enum" - } - - const ( - EnumFirst Enum = 1 - EnumSecond = 2 - ) - - func main() { - fmt.Println(EnumFirst) - fmt.Println(EnumSecond) - } - -This code will output - - an enum - 2 - -as \'EnumSecond\' has no explicit type, and thus defaults to \'int\'.`, - Since: "2019.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA9005": { - Title: `Trying to marshal a struct with no public fields nor custom marshaling`, - Text: ` -The \'encoding/json\' and \'encoding/xml\' packages only operate on exported -fields in structs, not unexported ones. It is usually an error to try -to (un)marshal structs that only consist of unexported fields. - -This check will not flag calls involving types that define custom -marshaling behavior, e.g. via \'MarshalJSON\' methods. It will also not -flag empty structs.`, - Since: "2019.2", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAll, - }, - - "SA9006": { - Title: `Dubious bit shifting of a fixed size integer value`, - Text: `Bit shifting a value past its size will always clear the value. - -For instance: - - v := int8(42) - v >>= 8 - -will always result in 0. - -This check flags bit shifting operations on fixed size integer values only. -That is, int, uint and uintptr are never flagged to avoid potential false -positives in somewhat exotic but valid bit twiddling tricks: - - // Clear any value above 32 bits if integers are more than 32 bits. - func f(i int) int { - v := i >> 32 - v = v << 32 - return i-v - }`, - Since: "2020.2", - Severity: lint.SeverityWarning, - // Technically this should be MergeIfAll, because the type of - // v might be different for different build tags. Practically, - // don't write code that depends on that. - MergeIf: lint.MergeIfAny, - }, - - "SA9007": { - Title: "Deleting a directory that shouldn't be deleted", - Text: ` -It is virtually never correct to delete system directories such as -/tmp or the user's home directory. However, it can be fairly easy to -do by mistake, for example by mistakingly using \'os.TempDir\' instead -of \'ioutil.TempDir\', or by forgetting to add a suffix to the result -of \'os.UserHomeDir\'. - -Writing - - d := os.TempDir() - defer os.RemoveAll(d) - -in your unit tests will have a devastating effect on the stability of your system. - -This check flags attempts at deleting the following directories: - -- os.TempDir -- os.UserCacheDir -- os.UserConfigDir -- os.UserHomeDir -`, - Since: "2022.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, - - "SA9008": { - Title: `\'else\' branch of a type assertion is probably not reading the right value`, - Text: ` -When declaring variables as part of an \'if\' statement (like in \"if -foo := ...; foo {\"), the same variables will also be in the scope of -the \'else\' branch. This means that in the following example - - if x, ok := x.(int); ok { - // ... - } else { - fmt.Printf("unexpected type %T", x) - } - -\'x\' in the \'else\' branch will refer to the \'x\' from \'x, ok -:=\'; it will not refer to the \'x\' that is being type-asserted. The -result of a failed type assertion is the zero value of the type that -is being asserted to, so \'x\' in the else branch will always have the -value \'0\' and the type \'int\'. -`, - Since: "2022.1", - Severity: lint.SeverityWarning, - MergeIf: lint.MergeIfAny, - }, -}) diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/fakereflect/fakereflect.go b/tools/vendor/honnef.co/go/tools/staticcheck/fakereflect/fakereflect.go index 7f8fd47991..5fdf15b253 100644 --- a/tools/vendor/honnef.co/go/tools/staticcheck/fakereflect/fakereflect.go +++ b/tools/vendor/honnef.co/go/tools/staticcheck/fakereflect/fakereflect.go @@ -84,7 +84,7 @@ func (t TypeAndCanAddr) IsStruct() bool { } func (t TypeAndCanAddr) Name() string { - named, ok := t.Type.(*types.Named) + named, ok := types.Unalias(t.Type).(*types.Named) if !ok { return "" } diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/fakexml/marshal.go b/tools/vendor/honnef.co/go/tools/staticcheck/fakexml/marshal.go index 64fce5f5b2..6a30d7907d 100644 --- a/tools/vendor/honnef.co/go/tools/staticcheck/fakexml/marshal.go +++ b/tools/vendor/honnef.co/go/tools/staticcheck/fakexml/marshal.go @@ -56,17 +56,17 @@ func implementsMarshaler(v fakereflect.TypeAndCanAddr) bool { if params.Len() != 2 { return false } - if !typeutil.IsType(params.At(0).Type(), "*encoding/xml.Encoder") { + if !typeutil.IsPointerToTypeWithName(params.At(0).Type(), "encoding/xml.Encoder") { return false } - if !typeutil.IsType(params.At(1).Type(), "encoding/xml.StartElement") { + if !typeutil.IsTypeWithName(params.At(1).Type(), "encoding/xml.StartElement") { return false } rets := fn.Type().(*types.Signature).Results() if rets.Len() != 1 { return false } - if !typeutil.IsType(rets.At(0).Type(), "error") { + if !typeutil.IsTypeWithName(rets.At(0).Type(), "error") { return false } return true @@ -86,17 +86,17 @@ func implementsMarshalerAttr(v fakereflect.TypeAndCanAddr) bool { if params.Len() != 1 { return false } - if !typeutil.IsType(params.At(0).Type(), "encoding/xml.Name") { + if !typeutil.IsTypeWithName(params.At(0).Type(), "encoding/xml.Name") { return false } rets := fn.Type().(*types.Signature).Results() if rets.Len() != 2 { return false } - if !typeutil.IsType(rets.At(0).Type(), "encoding/xml.Attr") { + if !typeutil.IsTypeWithName(rets.At(0).Type(), "encoding/xml.Attr") { return false } - if !typeutil.IsType(rets.At(1).Type(), "error") { + if !typeutil.IsTypeWithName(rets.At(1).Type(), "error") { return false } return true @@ -279,7 +279,7 @@ func (e *Encoder) marshalAttr(start *StartElement, name Name, val fakereflect.Ty return nil } - if typeutil.IsType(val.Type, "encoding/xml.Attr") { + if typeutil.IsTypeWithName(val.Type, "encoding/xml.Attr") { return nil } @@ -350,7 +350,7 @@ func (e *Encoder) marshalStruct(tinfo *typeInfo, val fakereflect.TypeAndCanAddr, case fInnerXML: vf = indirect(vf) - if typeutil.IsType(vf.Type, "[]byte") || typeutil.IsType(vf.Type, "string") { + if t, ok := vf.Type.(*types.Slice); (ok && types.Identical(t.Elem(), types.Typ[types.Byte])) || types.Identical(vf.Type, types.Typ[types.String]) { continue } diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/fakexml/typeinfo.go b/tools/vendor/honnef.co/go/tools/staticcheck/fakexml/typeinfo.go index 07e5abe791..cbde81bd91 100644 --- a/tools/vendor/honnef.co/go/tools/staticcheck/fakexml/typeinfo.go +++ b/tools/vendor/honnef.co/go/tools/staticcheck/fakexml/typeinfo.go @@ -6,11 +6,11 @@ package fakexml import ( "fmt" - "go/types" "strconv" "strings" "sync" + "honnef.co/go/tools/go/types/typeutil" "honnef.co/go/tools/staticcheck/fakereflect" ) @@ -80,8 +80,7 @@ func getTypeInfo(typ fakereflect.TypeAndCanAddr) (*typeInfo, error) { } tinfo := &typeInfo{} - named, ok := typ.Type.(*types.Named) - if typ.IsStruct() && !(ok && named.Obj().Pkg().Path() == "encoding/xml" && named.Obj().Name() == "Name") { + if typ.IsStruct() && !typeutil.IsTypeWithName(typ.Type, "encoding/xml.Name") { n := typ.NumField() for i := 0; i < n; i++ { f := typ.Field(i) diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/lint.go b/tools/vendor/honnef.co/go/tools/staticcheck/lint.go deleted file mode 100644 index b7c0f91375..0000000000 --- a/tools/vendor/honnef.co/go/tools/staticcheck/lint.go +++ /dev/null @@ -1,5237 +0,0 @@ -package staticcheck - -import ( - "fmt" - "go/ast" - "go/constant" - "go/token" - "go/types" - htmltemplate "html/template" - "net/http" - "os" - "reflect" - "regexp" - "regexp/syntax" - "sort" - "strconv" - "strings" - texttemplate "text/template" - "unicode" - - "honnef.co/go/tools/analysis/code" - "honnef.co/go/tools/analysis/edit" - "honnef.co/go/tools/analysis/facts/deprecated" - "honnef.co/go/tools/analysis/facts/generated" - "honnef.co/go/tools/analysis/facts/nilness" - "honnef.co/go/tools/analysis/facts/purity" - "honnef.co/go/tools/analysis/facts/typedness" - "honnef.co/go/tools/analysis/lint" - "honnef.co/go/tools/analysis/report" - "honnef.co/go/tools/go/ast/astutil" - "honnef.co/go/tools/go/ir" - "honnef.co/go/tools/go/ir/irutil" - "honnef.co/go/tools/go/types/typeutil" - "honnef.co/go/tools/internal/passes/buildir" - "honnef.co/go/tools/internal/sharedcheck" - "honnef.co/go/tools/knowledge" - "honnef.co/go/tools/pattern" - "honnef.co/go/tools/printf" - "honnef.co/go/tools/staticcheck/fakejson" - "honnef.co/go/tools/staticcheck/fakereflect" - "honnef.co/go/tools/staticcheck/fakexml" - - "golang.org/x/exp/typeparams" - "golang.org/x/tools/go/analysis" - "golang.org/x/tools/go/analysis/passes/inspect" - "golang.org/x/tools/go/ast/inspector" -) - -func checkSortSlice(call *Call) { - c := call.Instr.Common().StaticCallee() - arg := call.Args[0] - - T := arg.Value.Value.Type().Underlying() - switch T.(type) { - case *types.Interface: - // we don't know. - // TODO(dh): if the value is a phi node we can look at its edges - if k, ok := arg.Value.Value.(*ir.Const); ok && k.Value == nil { - // literal nil, e.g. sort.Sort(nil, ...) - arg.Invalid(fmt.Sprintf("cannot call %s on nil literal", c)) - } - case *types.Slice: - // this is fine - default: - // this is not fine - arg.Invalid(fmt.Sprintf("%s must only be called on slices, was called on %s", c, T)) - } -} - -func validRegexp(call *Call) { - arg := call.Args[0] - err := ValidateRegexp(arg.Value) - if err != nil { - arg.Invalid(err.Error()) - } -} - -type runeSlice []rune - -func (rs runeSlice) Len() int { return len(rs) } -func (rs runeSlice) Less(i int, j int) bool { return rs[i] < rs[j] } -func (rs runeSlice) Swap(i int, j int) { rs[i], rs[j] = rs[j], rs[i] } - -func utf8Cutset(call *Call) { - arg := call.Args[1] - if InvalidUTF8(arg.Value) { - arg.Invalid(MsgInvalidUTF8) - } -} - -func uniqueCutset(call *Call) { - arg := call.Args[1] - if !UniqueStringCutset(arg.Value) { - arg.Invalid(MsgNonUniqueCutset) - } -} - -func unmarshalPointer(name string, arg int) CallCheck { - return func(call *Call) { - if !Pointer(call.Args[arg].Value) { - call.Args[arg].Invalid(fmt.Sprintf("%s expects to unmarshal into a pointer, but the provided value is not a pointer", name)) - } - } -} - -func pointlessIntMath(call *Call) { - if ConvertedFromInt(call.Args[0].Value) { - call.Invalid(fmt.Sprintf("calling %s on a converted integer is pointless", irutil.CallName(call.Instr.Common()))) - } -} - -func checkValidHostPort(arg int) CallCheck { - return func(call *Call) { - if !ValidHostPort(call.Args[arg].Value) { - call.Args[arg].Invalid(MsgInvalidHostPort) - } - } -} - -var ( - checkRegexpRules = map[string]CallCheck{ - "regexp.MustCompile": validRegexp, - "regexp.Compile": validRegexp, - "regexp.Match": validRegexp, - "regexp.MatchReader": validRegexp, - "regexp.MatchString": validRegexp, - } - - checkTimeParseRules = map[string]CallCheck{ - "time.Parse": func(call *Call) { - arg := call.Args[knowledge.Arg("time.Parse.layout")] - err := ValidateTimeLayout(arg.Value) - if err != nil { - arg.Invalid(err.Error()) - } - }, - } - - checkEncodingBinaryRules = map[string]CallCheck{ - "encoding/binary.Write": func(call *Call) { - arg := call.Args[knowledge.Arg("encoding/binary.Write.data")] - if !CanBinaryMarshal(call.Pass, arg.Value) { - arg.Invalid(fmt.Sprintf("value of type %s cannot be used with binary.Write", arg.Value.Value.Type())) - } - }, - } - - checkURLsRules = map[string]CallCheck{ - "net/url.Parse": func(call *Call) { - arg := call.Args[knowledge.Arg("net/url.Parse.rawurl")] - err := ValidateURL(arg.Value) - if err != nil { - arg.Invalid(err.Error()) - } - }, - } - - checkSyncPoolValueRules = map[string]CallCheck{ - "(*sync.Pool).Put": func(call *Call) { - arg := call.Args[knowledge.Arg("(*sync.Pool).Put.x")] - typ := arg.Value.Value.Type() - _, isSlice := typ.Underlying().(*types.Slice) - if !typeutil.IsPointerLike(typ) || isSlice { - arg.Invalid("argument should be pointer-like to avoid allocations") - } - }, - } - - checkRegexpFindAllRules = map[string]CallCheck{ - "(*regexp.Regexp).FindAll": RepeatZeroTimes("a FindAll method", 1), - "(*regexp.Regexp).FindAllIndex": RepeatZeroTimes("a FindAll method", 1), - "(*regexp.Regexp).FindAllString": RepeatZeroTimes("a FindAll method", 1), - "(*regexp.Regexp).FindAllStringIndex": RepeatZeroTimes("a FindAll method", 1), - "(*regexp.Regexp).FindAllStringSubmatch": RepeatZeroTimes("a FindAll method", 1), - "(*regexp.Regexp).FindAllStringSubmatchIndex": RepeatZeroTimes("a FindAll method", 1), - "(*regexp.Regexp).FindAllSubmatch": RepeatZeroTimes("a FindAll method", 1), - "(*regexp.Regexp).FindAllSubmatchIndex": RepeatZeroTimes("a FindAll method", 1), - } - - checkUTF8CutsetRules = map[string]CallCheck{ - "strings.IndexAny": utf8Cutset, - "strings.LastIndexAny": utf8Cutset, - "strings.ContainsAny": utf8Cutset, - "strings.Trim": utf8Cutset, - "strings.TrimLeft": utf8Cutset, - "strings.TrimRight": utf8Cutset, - } - - checkUniqueCutsetRules = map[string]CallCheck{ - "strings.Trim": uniqueCutset, - "strings.TrimLeft": uniqueCutset, - "strings.TrimRight": uniqueCutset, - } - - checkUnmarshalPointerRules = map[string]CallCheck{ - "encoding/xml.Unmarshal": unmarshalPointer("xml.Unmarshal", 1), - "(*encoding/xml.Decoder).Decode": unmarshalPointer("Decode", 0), - "(*encoding/xml.Decoder).DecodeElement": unmarshalPointer("DecodeElement", 0), - "encoding/json.Unmarshal": unmarshalPointer("json.Unmarshal", 1), - "(*encoding/json.Decoder).Decode": unmarshalPointer("Decode", 0), - } - - checkUnbufferedSignalChanRules = map[string]CallCheck{ - "os/signal.Notify": func(call *Call) { - arg := call.Args[knowledge.Arg("os/signal.Notify.c")] - if UnbufferedChannel(arg.Value) { - arg.Invalid("the channel used with signal.Notify should be buffered") - } - }, - } - - checkMathIntRules = map[string]CallCheck{ - "math.Ceil": pointlessIntMath, - "math.Floor": pointlessIntMath, - "math.IsNaN": pointlessIntMath, - "math.Trunc": pointlessIntMath, - "math.IsInf": pointlessIntMath, - } - - checkStringsReplaceZeroRules = map[string]CallCheck{ - "strings.Replace": RepeatZeroTimes("strings.Replace", 3), - "bytes.Replace": RepeatZeroTimes("bytes.Replace", 3), - } - - checkListenAddressRules = map[string]CallCheck{ - "net/http.ListenAndServe": checkValidHostPort(0), - "net/http.ListenAndServeTLS": checkValidHostPort(0), - } - - checkBytesEqualIPRules = map[string]CallCheck{ - "bytes.Equal": func(call *Call) { - if ConvertedFrom(call.Args[knowledge.Arg("bytes.Equal.a")].Value, "net.IP") && - ConvertedFrom(call.Args[knowledge.Arg("bytes.Equal.b")].Value, "net.IP") { - call.Invalid("use net.IP.Equal to compare net.IPs, not bytes.Equal") - } - }, - } - - checkRegexpMatchLoopRules = map[string]CallCheck{ - "regexp.Match": loopedRegexp("regexp.Match"), - "regexp.MatchReader": loopedRegexp("regexp.MatchReader"), - "regexp.MatchString": loopedRegexp("regexp.MatchString"), - } - - checkNoopMarshal = map[string]CallCheck{ - // TODO(dh): should we really flag XML? Even an empty struct - // produces a non-zero amount of data, namely its type name. - // Let's see if we encounter any false positives. - // - // Also, should we flag gob? - "encoding/json.Marshal": checkNoopMarshalImpl(knowledge.Arg("json.Marshal.v"), "MarshalJSON", "MarshalText"), - "encoding/xml.Marshal": checkNoopMarshalImpl(knowledge.Arg("xml.Marshal.v"), "MarshalXML", "MarshalText"), - "(*encoding/json.Encoder).Encode": checkNoopMarshalImpl(knowledge.Arg("(*encoding/json.Encoder).Encode.v"), "MarshalJSON", "MarshalText"), - "(*encoding/xml.Encoder).Encode": checkNoopMarshalImpl(knowledge.Arg("(*encoding/xml.Encoder).Encode.v"), "MarshalXML", "MarshalText"), - - "encoding/json.Unmarshal": checkNoopMarshalImpl(knowledge.Arg("json.Unmarshal.v"), "UnmarshalJSON", "UnmarshalText"), - "encoding/xml.Unmarshal": checkNoopMarshalImpl(knowledge.Arg("xml.Unmarshal.v"), "UnmarshalXML", "UnmarshalText"), - "(*encoding/json.Decoder).Decode": checkNoopMarshalImpl(knowledge.Arg("(*encoding/json.Decoder).Decode.v"), "UnmarshalJSON", "UnmarshalText"), - "(*encoding/xml.Decoder).Decode": checkNoopMarshalImpl(knowledge.Arg("(*encoding/xml.Decoder).Decode.v"), "UnmarshalXML", "UnmarshalText"), - } - - checkUnsupportedMarshal = map[string]CallCheck{ - "encoding/json.Marshal": checkUnsupportedMarshalJSON, - "encoding/xml.Marshal": checkUnsupportedMarshalXML, - "(*encoding/json.Encoder).Encode": checkUnsupportedMarshalJSON, - "(*encoding/xml.Encoder).Encode": checkUnsupportedMarshalXML, - } - - checkAtomicAlignment = map[string]CallCheck{ - "sync/atomic.AddInt64": checkAtomicAlignmentImpl, - "sync/atomic.AddUint64": checkAtomicAlignmentImpl, - "sync/atomic.CompareAndSwapInt64": checkAtomicAlignmentImpl, - "sync/atomic.CompareAndSwapUint64": checkAtomicAlignmentImpl, - "sync/atomic.LoadInt64": checkAtomicAlignmentImpl, - "sync/atomic.LoadUint64": checkAtomicAlignmentImpl, - "sync/atomic.StoreInt64": checkAtomicAlignmentImpl, - "sync/atomic.StoreUint64": checkAtomicAlignmentImpl, - "sync/atomic.SwapInt64": checkAtomicAlignmentImpl, - "sync/atomic.SwapUint64": checkAtomicAlignmentImpl, - } - - // TODO(dh): detect printf wrappers - checkPrintfRules = map[string]CallCheck{ - "fmt.Errorf": func(call *Call) { checkPrintfCall(call, 0, 1) }, - "fmt.Printf": func(call *Call) { checkPrintfCall(call, 0, 1) }, - "fmt.Sprintf": func(call *Call) { checkPrintfCall(call, 0, 1) }, - "fmt.Fprintf": func(call *Call) { checkPrintfCall(call, 1, 2) }, - "golang.org/x/xerrors.Errorf": func(call *Call) { checkPrintfCall(call, 0, 1) }, - } - - checkSortSliceRules = map[string]CallCheck{ - "sort.Slice": checkSortSlice, - "sort.SliceIsSorted": checkSortSlice, - "sort.SliceStable": checkSortSlice, - } - - checkWithValueKeyRules = map[string]CallCheck{ - "context.WithValue": checkWithValueKey, - } - - checkStrconvRules = map[string]CallCheck{ - "strconv.ParseComplex": func(call *Call) { - validateComplexBitSize(call.Args[knowledge.Arg("strconv.ParseComplex.bitSize")]) - }, - "strconv.ParseFloat": func(call *Call) { - validateFloatBitSize(call.Args[knowledge.Arg("strconv.ParseFloat.bitSize")]) - }, - "strconv.ParseInt": func(call *Call) { - validateContinuousBitSize(call.Args[knowledge.Arg("strconv.ParseInt.bitSize")], 0, 64) - validateIntBaseAllowZero(call.Args[knowledge.Arg("strconv.ParseInt.base")]) - }, - "strconv.ParseUint": func(call *Call) { - validateContinuousBitSize(call.Args[knowledge.Arg("strconv.ParseUint.bitSize")], 0, 64) - validateIntBaseAllowZero(call.Args[knowledge.Arg("strconv.ParseUint.base")]) - }, - - "strconv.FormatComplex": func(call *Call) { - validateComplexFormat(call.Args[knowledge.Arg("strconv.FormatComplex.fmt")]) - validateComplexBitSize(call.Args[knowledge.Arg("strconv.FormatComplex.bitSize")]) - }, - "strconv.FormatFloat": func(call *Call) { - validateFloatFormat(call.Args[knowledge.Arg("strconv.FormatFloat.fmt")]) - validateFloatBitSize(call.Args[knowledge.Arg("strconv.FormatFloat.bitSize")]) - }, - "strconv.FormatInt": func(call *Call) { - validateIntBase(call.Args[knowledge.Arg("strconv.FormatInt.base")]) - }, - "strconv.FormatUint": func(call *Call) { - validateIntBase(call.Args[knowledge.Arg("strconv.FormatUint.base")]) - }, - - "strconv.AppendFloat": func(call *Call) { - validateFloatFormat(call.Args[knowledge.Arg("strconv.AppendFloat.fmt")]) - validateFloatBitSize(call.Args[knowledge.Arg("strconv.AppendFloat.bitSize")]) - }, - "strconv.AppendInt": func(call *Call) { - validateIntBase(call.Args[knowledge.Arg("strconv.AppendInt.base")]) - }, - "strconv.AppendUint": func(call *Call) { - validateIntBase(call.Args[knowledge.Arg("strconv.AppendUint.base")]) - }, - } -) - -func validateIntBase(arg *Argument) { - if c := extractConstExpectKind(arg.Value.Value, constant.Int); c != nil { - val, _ := constant.Int64Val(c.Value) - if val < 2 { - arg.Invalid("'base' must not be smaller than 2") - } - if val > 36 { - arg.Invalid("'base' must not be larger than 36") - } - } -} - -func validateIntBaseAllowZero(arg *Argument) { - if c := extractConstExpectKind(arg.Value.Value, constant.Int); c != nil { - val, _ := constant.Int64Val(c.Value) - if val < 2 && val != 0 { - arg.Invalid("'base' must not be smaller than 2, unless it is 0") - } - if val > 36 { - arg.Invalid("'base' must not be larger than 36") - } - } -} - -func validateComplexFormat(arg *Argument) { - validateFloatFormat(arg) -} - -func validateFloatFormat(arg *Argument) { - if c := extractConstExpectKind(arg.Value.Value, constant.Int); c != nil { - val, _ := constant.Int64Val(c.Value) - switch val { - case 'b', 'e', 'E', 'f', 'g', 'G', 'x', 'X': - default: - arg.Invalid(fmt.Sprintf("'fmt' argument is invalid: unknown format %q", val)) - } - } -} - -func validateComplexBitSize(arg *Argument) { validateDiscreetBitSize(arg, 64, 128) } -func validateFloatBitSize(arg *Argument) { validateDiscreetBitSize(arg, 32, 64) } - -func validateDiscreetBitSize(arg *Argument, size1 int, size2 int) { - if c := extractConstExpectKind(arg.Value.Value, constant.Int); c != nil { - val, _ := constant.Int64Val(c.Value) - if val != int64(size1) && val != int64(size2) { - arg.Invalid(fmt.Sprintf("'bitSize' argument is invalid, must be either %d or %d", size1, size2)) - } - } -} - -func validateContinuousBitSize(arg *Argument, min int, max int) { - if c := extractConstExpectKind(arg.Value.Value, constant.Int); c != nil { - val, _ := constant.Int64Val(c.Value) - if val < int64(min) || val > int64(max) { - arg.Invalid(fmt.Sprintf("'bitSize' argument is invalid, must be within %d and %d", min, max)) - } - } -} - -func checkPrintfCall(call *Call, fIdx, vIdx int) { - f := call.Args[fIdx] - var args []ir.Value - switch v := call.Args[vIdx].Value.Value.(type) { - case *ir.Slice: - var ok bool - args, ok = irutil.Vararg(v) - if !ok { - // We don't know what the actual arguments to the function are - return - } - case *ir.Const: - // nil, i.e. no arguments - default: - // We don't know what the actual arguments to the function are - return - } - checkPrintfCallImpl(f, f.Value.Value, args) -} - -type verbFlag int - -const ( - isInt verbFlag = 1 << iota - isBool - isFP - isString - isPointer - // Verbs that accept "pseudo pointers" will sometimes dereference - // non-nil pointers. For example, %x on a non-nil *struct will print the - // individual fields, but on a nil pointer it will print the address. - isPseudoPointer - isSlice - isAny - noRecurse -) - -var verbs = [...]verbFlag{ - 'b': isPseudoPointer | isInt | isFP, - 'c': isInt, - 'd': isPseudoPointer | isInt, - 'e': isFP, - 'E': isFP, - 'f': isFP, - 'F': isFP, - 'g': isFP, - 'G': isFP, - 'o': isPseudoPointer | isInt, - 'O': isPseudoPointer | isInt, - 'p': isSlice | isPointer | noRecurse, - 'q': isInt | isString, - 's': isString, - 't': isBool, - 'T': isAny, - 'U': isInt, - 'v': isAny, - 'X': isPseudoPointer | isInt | isFP | isString, - 'x': isPseudoPointer | isInt | isFP | isString, -} - -func checkPrintfCallImpl(carg *Argument, f ir.Value, args []ir.Value) { - var msCache *typeutil.MethodSetCache - if f.Parent() != nil { - msCache = &f.Parent().Prog.MethodSets - } - - elem := func(T types.Type, verb rune) ([]types.Type, bool) { - if verbs[verb]&noRecurse != 0 { - return []types.Type{T}, false - } - switch T := T.(type) { - case *types.Slice: - if verbs[verb]&isSlice != 0 { - return []types.Type{T}, false - } - if verbs[verb]&isString != 0 && typeutil.IsType(T.Elem().Underlying(), "byte") { - return []types.Type{T}, false - } - return []types.Type{T.Elem()}, true - case *types.Map: - key := T.Key() - val := T.Elem() - return []types.Type{key, val}, true - case *types.Struct: - out := make([]types.Type, 0, T.NumFields()) - for i := 0; i < T.NumFields(); i++ { - out = append(out, T.Field(i).Type()) - } - return out, true - case *types.Array: - return []types.Type{T.Elem()}, true - default: - return []types.Type{T}, false - } - } - isInfo := func(T types.Type, info types.BasicInfo) bool { - basic, ok := T.Underlying().(*types.Basic) - return ok && basic.Info()&info != 0 - } - - isFormatter := func(T types.Type, ms *types.MethodSet) bool { - sel := ms.Lookup(nil, "Format") - if sel == nil { - return false - } - fn, ok := sel.Obj().(*types.Func) - if !ok { - // should be unreachable - return false - } - sig := fn.Type().(*types.Signature) - if sig.Params().Len() != 2 { - return false - } - // TODO(dh): check the types of the arguments for more - // precision - if sig.Results().Len() != 0 { - return false - } - return true - } - - var seen typeutil.Map[struct{}] - var checkType func(verb rune, T types.Type, top bool) bool - checkType = func(verb rune, T types.Type, top bool) bool { - if top { - seen = typeutil.Map[struct{}]{} - } - if _, ok := seen.At(T); ok { - return true - } - seen.Set(T, struct{}{}) - if int(verb) >= len(verbs) { - // Unknown verb - return true - } - - flags := verbs[verb] - if flags == 0 { - // Unknown verb - return true - } - - ms := msCache.MethodSet(T) - if isFormatter(T, ms) { - // the value is responsible for formatting itself - return true - } - - if flags&isString != 0 && (types.Implements(T, knowledge.Interfaces["fmt.Stringer"]) || types.Implements(T, knowledge.Interfaces["error"])) { - // Check for stringer early because we're about to dereference - return true - } - - T = T.Underlying() - if flags&(isPointer|isPseudoPointer) == 0 && top { - T = typeutil.Dereference(T) - } - if flags&isPseudoPointer != 0 && top { - t := typeutil.Dereference(T) - if _, ok := t.Underlying().(*types.Struct); ok { - T = t - } - } - - if _, ok := T.(*types.Interface); ok { - // We don't know what's in the interface - return true - } - - var info types.BasicInfo - if flags&isInt != 0 { - info |= types.IsInteger - } - if flags&isBool != 0 { - info |= types.IsBoolean - } - if flags&isFP != 0 { - info |= types.IsFloat | types.IsComplex - } - if flags&isString != 0 { - info |= types.IsString - } - - if info != 0 && isInfo(T, info) { - return true - } - - if flags&isString != 0 { - isStringyElem := func(typ types.Type) bool { - if typ, ok := typ.Underlying().(*types.Basic); ok { - return typ.Kind() == types.Byte - } - return false - } - switch T := T.(type) { - case *types.Slice: - if isStringyElem(T.Elem()) { - return true - } - case *types.Array: - if isStringyElem(T.Elem()) { - return true - } - } - if types.Implements(T, knowledge.Interfaces["fmt.Stringer"]) || types.Implements(T, knowledge.Interfaces["error"]) { - return true - } - } - - if flags&isPointer != 0 && typeutil.IsPointerLike(T) { - return true - } - if flags&isPseudoPointer != 0 { - switch U := T.Underlying().(type) { - case *types.Pointer: - if !top { - return true - } - - if _, ok := U.Elem().Underlying().(*types.Struct); !ok { - // TODO(dh): can this condition ever be false? For - // *T, if T is a struct, we'll already have - // dereferenced it, meaning the *types.Pointer - // branch couldn't have been taken. For T that - // aren't structs, this condition will always - // evaluate to true. - return true - } - case *types.Chan, *types.Signature: - // Channels and functions are always treated as - // pointers and never recursed into. - return true - case *types.Basic: - if U.Kind() == types.UnsafePointer { - return true - } - case *types.Interface: - // we will already have bailed if the type is an - // interface. - panic("unreachable") - default: - // other pointer-like types, such as maps or slices, - // will be printed element-wise. - } - } - - if flags&isSlice != 0 { - if _, ok := T.(*types.Slice); ok { - return true - } - } - - if flags&isAny != 0 { - return true - } - - elems, ok := elem(T.Underlying(), verb) - if !ok { - return false - } - for _, elem := range elems { - if !checkType(verb, elem, false) { - return false - } - } - - return true - } - - k, ok := irutil.Flatten(f).(*ir.Const) - if !ok { - return - } - actions, err := printf.Parse(constant.StringVal(k.Value)) - if err != nil { - carg.Invalid("couldn't parse format string") - return - } - - ptr := 1 - hasExplicit := false - - checkStar := func(verb printf.Verb, star printf.Argument) bool { - if star, ok := star.(printf.Star); ok { - idx := 0 - if star.Index == -1 { - idx = ptr - ptr++ - } else { - hasExplicit = true - idx = star.Index - ptr = star.Index + 1 - } - if idx == 0 { - carg.Invalid(fmt.Sprintf("Printf format %s reads invalid arg 0; indices are 1-based", verb.Raw)) - return false - } - if idx > len(args) { - carg.Invalid( - fmt.Sprintf("Printf format %s reads arg #%d, but call has only %d args", - verb.Raw, idx, len(args))) - return false - } - if arg, ok := args[idx-1].(*ir.MakeInterface); ok { - if !isInfo(arg.X.Type(), types.IsInteger) { - carg.Invalid(fmt.Sprintf("Printf format %s reads non-int arg #%d as argument of *", verb.Raw, idx)) - } - } - } - return true - } - - // We only report one problem per format string. Making a - // mistake with an index tends to invalidate all future - // implicit indices. - for _, action := range actions { - verb, ok := action.(printf.Verb) - if !ok { - continue - } - - if !checkStar(verb, verb.Width) || !checkStar(verb, verb.Precision) { - return - } - - off := ptr - if verb.Value != -1 { - hasExplicit = true - off = verb.Value - } - if off > len(args) { - carg.Invalid( - fmt.Sprintf("Printf format %s reads arg #%d, but call has only %d args", - verb.Raw, off, len(args))) - return - } else if verb.Value == 0 && verb.Letter != '%' { - carg.Invalid(fmt.Sprintf("Printf format %s reads invalid arg 0; indices are 1-based", verb.Raw)) - return - } else if off != 0 { - arg, ok := args[off-1].(*ir.MakeInterface) - if ok { - if !checkType(verb.Letter, arg.X.Type(), true) { - carg.Invalid(fmt.Sprintf("Printf format %s has arg #%d of wrong type %s", - verb.Raw, ptr, args[ptr-1].(*ir.MakeInterface).X.Type())) - return - } - } - } - - switch verb.Value { - case -1: - // Consume next argument - ptr++ - case 0: - // Don't consume any arguments - default: - ptr = verb.Value + 1 - } - } - - if !hasExplicit && ptr <= len(args) { - carg.Invalid(fmt.Sprintf("Printf call needs %d args but has %d args", ptr-1, len(args))) - } -} - -func checkAtomicAlignmentImpl(call *Call) { - sizes := call.Pass.TypesSizes - if sizes.Sizeof(types.Typ[types.Uintptr]) != 4 { - // Not running on a 32-bit platform - return - } - v, ok := irutil.Flatten(call.Args[0].Value.Value).(*ir.FieldAddr) - if !ok { - // TODO(dh): also check indexing into arrays and slices - return - } - T := v.X.Type().Underlying().(*types.Pointer).Elem().Underlying().(*types.Struct) - fields := make([]*types.Var, 0, T.NumFields()) - for i := 0; i < T.NumFields() && i <= v.Field; i++ { - fields = append(fields, T.Field(i)) - } - - off := sizes.Offsetsof(fields)[v.Field] - if off%8 != 0 { - msg := fmt.Sprintf("address of non 64-bit aligned field %s passed to %s", - T.Field(v.Field).Name(), - irutil.CallName(call.Instr.Common())) - call.Invalid(msg) - } -} - -func checkNoopMarshalImpl(argN int, meths ...string) CallCheck { - return func(call *Call) { - if code.IsGenerated(call.Pass, call.Instr.Pos()) { - return - } - arg := call.Args[argN] - T := arg.Value.Value.Type() - Ts, ok := typeutil.Dereference(T).Underlying().(*types.Struct) - if !ok { - return - } - if Ts.NumFields() == 0 { - return - } - fields := typeutil.FlattenFields(Ts) - for _, field := range fields { - if field.Var.Exported() { - return - } - } - // OPT(dh): we could use a method set cache here - ms := call.Instr.Parent().Prog.MethodSets.MethodSet(T) - // TODO(dh): we're not checking the signature, which can cause false negatives. - // This isn't a huge problem, however, since vet complains about incorrect signatures. - for _, meth := range meths { - if ms.Lookup(nil, meth) != nil { - return - } - } - arg.Invalid(fmt.Sprintf("struct type '%s' doesn't have any exported fields, nor custom marshaling", typeutil.Dereference(T))) - } -} - -func checkUnsupportedMarshalJSON(call *Call) { - arg := call.Args[0] - T := arg.Value.Value.Type() - if err := fakejson.Marshal(T); err != nil { - typ := types.TypeString(err.Type, types.RelativeTo(arg.Value.Value.Parent().Pkg.Pkg)) - if err.Path == "x" { - arg.Invalid(fmt.Sprintf("trying to marshal unsupported type %s", typ)) - } else { - arg.Invalid(fmt.Sprintf("trying to marshal unsupported type %s, via %s", typ, err.Path)) - } - } -} - -func checkUnsupportedMarshalXML(call *Call) { - arg := call.Args[0] - T := arg.Value.Value.Type() - if err := fakexml.Marshal(T); err != nil { - switch err := err.(type) { - case *fakexml.UnsupportedTypeError: - typ := types.TypeString(err.Type, types.RelativeTo(arg.Value.Value.Parent().Pkg.Pkg)) - if err.Path == "x" { - arg.Invalid(fmt.Sprintf("trying to marshal unsupported type %s", typ)) - } else { - arg.Invalid(fmt.Sprintf("trying to marshal unsupported type %s, via %s", typ, err.Path)) - } - case *fakexml.CyclicTypeError: - typ := types.TypeString(err.Type, types.RelativeTo(arg.Value.Value.Parent().Pkg.Pkg)) - if err.Path == "x" { - arg.Invalid(fmt.Sprintf("trying to marshal cyclic type %s", typ)) - } else { - arg.Invalid(fmt.Sprintf("trying to marshal cyclic type %s, via %s", typ, err.Path)) - } - case *fakexml.TagPathError: - // Vet does a better job at reporting this error, because it can flag the actual struct tags, not just the call to Marshal - default: - // These errors get reported by SA5008 instead, which can flag the actual fields, independently of calls to xml.Marshal - } - } -} - -func isInLoop(b *ir.BasicBlock) bool { - sets := irutil.FindLoops(b.Parent()) - for _, set := range sets { - if set.Has(b) { - return true - } - } - return false -} - -func CheckUntrappableSignal(pass *analysis.Pass) (interface{}, error) { - isSignal := func(pass *analysis.Pass, expr ast.Expr, name string) bool { - if expr, ok := expr.(*ast.SelectorExpr); ok { - return code.SelectorName(pass, expr) == name - } else { - return false - } - } - - fn := func(node ast.Node) { - call := node.(*ast.CallExpr) - if !code.IsCallToAny(pass, call, - "os/signal.Ignore", "os/signal.Notify", "os/signal.Reset") { - return - } - - hasSigterm := false - for _, arg := range call.Args { - if conv, ok := arg.(*ast.CallExpr); ok && isSignal(pass, conv.Fun, "os.Signal") { - arg = conv.Args[0] - } - - if isSignal(pass, arg, "syscall.SIGTERM") { - hasSigterm = true - break - } - - } - for i, arg := range call.Args { - if conv, ok := arg.(*ast.CallExpr); ok && isSignal(pass, conv.Fun, "os.Signal") { - arg = conv.Args[0] - } - - if isSignal(pass, arg, "os.Kill") || isSignal(pass, arg, "syscall.SIGKILL") { - var fixes []analysis.SuggestedFix - if !hasSigterm { - nargs := make([]ast.Expr, len(call.Args)) - for j, a := range call.Args { - if i == j { - nargs[j] = edit.Selector("syscall", "SIGTERM") - } else { - nargs[j] = a - } - } - ncall := *call - ncall.Args = nargs - fixes = append(fixes, edit.Fix(fmt.Sprintf("use syscall.SIGTERM instead of %s", report.Render(pass, arg)), edit.ReplaceWithNode(pass.Fset, call, &ncall))) - } - nargs := make([]ast.Expr, 0, len(call.Args)) - for j, a := range call.Args { - if i == j { - continue - } - nargs = append(nargs, a) - } - ncall := *call - ncall.Args = nargs - fixes = append(fixes, edit.Fix(fmt.Sprintf("remove %s from list of arguments", report.Render(pass, arg)), edit.ReplaceWithNode(pass.Fset, call, &ncall))) - report.Report(pass, arg, fmt.Sprintf("%s cannot be trapped (did you mean syscall.SIGTERM?)", report.Render(pass, arg)), report.Fixes(fixes...)) - } - if isSignal(pass, arg, "syscall.SIGSTOP") { - nargs := make([]ast.Expr, 0, len(call.Args)-1) - for j, a := range call.Args { - if i == j { - continue - } - nargs = append(nargs, a) - } - ncall := *call - ncall.Args = nargs - report.Report(pass, arg, "syscall.SIGSTOP cannot be trapped", report.Fixes(edit.Fix("remove syscall.SIGSTOP from list of arguments", edit.ReplaceWithNode(pass.Fset, call, &ncall)))) - } - } - } - code.Preorder(pass, fn, (*ast.CallExpr)(nil)) - return nil, nil -} - -func CheckTemplate(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - call := node.(*ast.CallExpr) - // OPT(dh): use integer for kind - var kind string - switch code.CallName(pass, call) { - case "(*text/template.Template).Parse": - kind = "text" - case "(*html/template.Template).Parse": - kind = "html" - default: - return - } - sel := call.Fun.(*ast.SelectorExpr) - if !code.IsCallToAny(pass, sel.X, "text/template.New", "html/template.New") { - // TODO(dh): this is a cheap workaround for templates with - // different delims. A better solution with less false - // negatives would use data flow analysis to see where the - // template comes from and where it has been - return - } - s, ok := code.ExprToString(pass, call.Args[knowledge.Arg("(*text/template.Template).Parse.text")]) - if !ok { - return - } - var err error - switch kind { - case "text": - _, err = texttemplate.New("").Parse(s) - case "html": - _, err = htmltemplate.New("").Parse(s) - } - if err != nil { - // TODO(dominikh): whitelist other parse errors, if any - if strings.Contains(err.Error(), "unexpected") || - strings.Contains(err.Error(), "bad character") { - report.Report(pass, call.Args[knowledge.Arg("(*text/template.Template).Parse.text")], err.Error()) - } - } - } - code.Preorder(pass, fn, (*ast.CallExpr)(nil)) - return nil, nil -} - -var ( - checkTimeSleepConstantPatternQ = pattern.MustParse(`(CallExpr (Symbol "time.Sleep") lit@(IntegerLiteral value))`) - checkTimeSleepConstantPatternRns = pattern.MustParse(`(BinaryExpr duration "*" (SelectorExpr (Ident "time") (Ident "Nanosecond")))`) - checkTimeSleepConstantPatternRs = pattern.MustParse(`(BinaryExpr duration "*" (SelectorExpr (Ident "time") (Ident "Second")))`) -) - -func CheckTimeSleepConstant(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - m, ok := code.Match(pass, checkTimeSleepConstantPatternQ, node) - if !ok { - return - } - n, ok := constant.Int64Val(m.State["value"].(types.TypeAndValue).Value) - if !ok { - return - } - if n == 0 || n > 120 { - // time.Sleep(0) is a seldom used pattern in concurrency - // tests. >120 might be intentional. 120 was chosen - // because the user could've meant 2 minutes. - return - } - - lit := m.State["lit"].(ast.Node) - report.Report(pass, lit, - fmt.Sprintf("sleeping for %d nanoseconds is probably a bug; be explicit if it isn't", n), report.Fixes( - edit.Fix("explicitly use nanoseconds", edit.ReplaceWithPattern(pass.Fset, lit, checkTimeSleepConstantPatternRns, pattern.State{"duration": lit})), - edit.Fix("use seconds", edit.ReplaceWithPattern(pass.Fset, lit, checkTimeSleepConstantPatternRs, pattern.State{"duration": lit})))) - } - code.Preorder(pass, fn, (*ast.CallExpr)(nil)) - return nil, nil -} - -var checkWaitgroupAddQ = pattern.MustParse(` - (GoStmt - (CallExpr - (FuncLit - _ - call@(CallExpr (Symbol "(*sync.WaitGroup).Add") _):_) _))`) - -func CheckWaitgroupAdd(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - if m, ok := code.Match(pass, checkWaitgroupAddQ, node); ok { - call := m.State["call"].(ast.Node) - report.Report(pass, call, fmt.Sprintf("should call %s before starting the goroutine to avoid a race", report.Render(pass, call))) - } - } - code.Preorder(pass, fn, (*ast.GoStmt)(nil)) - return nil, nil -} - -func CheckInfiniteEmptyLoop(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - loop := node.(*ast.ForStmt) - if len(loop.Body.List) != 0 || loop.Post != nil { - return - } - - if loop.Init != nil { - // TODO(dh): this isn't strictly necessary, it just makes - // the check easier. - return - } - // An empty loop is bad news in two cases: 1) The loop has no - // condition. In that case, it's just a loop that spins - // forever and as fast as it can, keeping a core busy. 2) The - // loop condition only consists of variable or field reads and - // operators on those. The only way those could change their - // value is with unsynchronised access, which constitutes a - // data race. - // - // If the condition contains any function calls, its behaviour - // is dynamic and the loop might terminate. Similarly for - // channel receives. - - if loop.Cond != nil { - if code.MayHaveSideEffects(pass, loop.Cond, nil) { - return - } - if ident, ok := loop.Cond.(*ast.Ident); ok { - if k, ok := pass.TypesInfo.ObjectOf(ident).(*types.Const); ok { - if !constant.BoolVal(k.Val()) { - // don't flag `for false {}` loops. They're a debug aid. - return - } - } - } - report.Report(pass, loop, "loop condition never changes or has a race condition") - } - report.Report(pass, loop, "this loop will spin, using 100% CPU", report.ShortRange()) - } - code.Preorder(pass, fn, (*ast.ForStmt)(nil)) - return nil, nil -} - -func CheckDeferInInfiniteLoop(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - mightExit := false - var defers []ast.Stmt - loop := node.(*ast.ForStmt) - if loop.Cond != nil { - return - } - fn2 := func(node ast.Node) bool { - switch stmt := node.(type) { - case *ast.ReturnStmt: - mightExit = true - return false - case *ast.BranchStmt: - // TODO(dominikh): if this sees a break in a switch or - // select, it doesn't check if it breaks the loop or - // just the select/switch. This causes some false - // negatives. - if stmt.Tok == token.BREAK { - mightExit = true - return false - } - case *ast.DeferStmt: - defers = append(defers, stmt) - case *ast.FuncLit: - // Don't look into function bodies - return false - } - return true - } - ast.Inspect(loop.Body, fn2) - if mightExit { - return - } - for _, stmt := range defers { - report.Report(pass, stmt, "defers in this infinite loop will never run") - } - } - code.Preorder(pass, fn, (*ast.ForStmt)(nil)) - return nil, nil -} - -func CheckDubiousDeferInChannelRangeLoop(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - loop := node.(*ast.RangeStmt) - typ := pass.TypesInfo.TypeOf(loop.X) - _, ok := typeutil.CoreType(typ).(*types.Chan) - if !ok { - return - } - - stmts := []*ast.DeferStmt{} - exits := false - fn2 := func(node ast.Node) bool { - switch stmt := node.(type) { - case *ast.DeferStmt: - stmts = append(stmts, stmt) - case *ast.FuncLit: - // Don't look into function bodies - return false - case *ast.ReturnStmt: - exits = true - case *ast.BranchStmt: - exits = node.(*ast.BranchStmt).Tok == token.BREAK - } - return true - } - ast.Inspect(loop.Body, fn2) - - if exits { - return - } - for _, stmt := range stmts { - report.Report(pass, stmt, "defers in this range loop won't run unless the channel gets closed") - } - } - code.Preorder(pass, fn, (*ast.RangeStmt)(nil)) - return nil, nil -} - -func CheckTestMainExit(pass *analysis.Pass) (interface{}, error) { - if code.IsGoVersion(pass, 15) { - // Beginning with Go 1.15, the test framework will call - // os.Exit for us. - return nil, nil - } - - var ( - fnmain ast.Node - callsExit bool - callsRun bool - arg types.Object - ) - fn := func(node ast.Node, push bool) bool { - if !push { - if fnmain != nil && node == fnmain { - if !callsExit && callsRun { - report.Report(pass, fnmain, "TestMain should call os.Exit to set exit code") - } - fnmain = nil - callsExit = false - callsRun = false - arg = nil - } - return true - } - - switch node := node.(type) { - case *ast.FuncDecl: - if fnmain != nil { - return true - } - if !isTestMain(pass, node) { - return false - } - fnmain = node - arg = pass.TypesInfo.ObjectOf(node.Type.Params.List[0].Names[0]) - return true - case *ast.CallExpr: - if code.IsCallTo(pass, node, "os.Exit") { - callsExit = true - return false - } - sel, ok := node.Fun.(*ast.SelectorExpr) - if !ok { - return true - } - ident, ok := sel.X.(*ast.Ident) - if !ok { - return true - } - if arg != pass.TypesInfo.ObjectOf(ident) { - return true - } - if sel.Sel.Name == "Run" { - callsRun = true - return false - } - return true - default: - lint.ExhaustiveTypeSwitch(node) - return true - } - } - pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Nodes([]ast.Node{(*ast.FuncDecl)(nil), (*ast.CallExpr)(nil)}, fn) - return nil, nil -} - -func isTestMain(pass *analysis.Pass, decl *ast.FuncDecl) bool { - if decl.Name.Name != "TestMain" { - return false - } - if len(decl.Type.Params.List) != 1 { - return false - } - arg := decl.Type.Params.List[0] - if len(arg.Names) != 1 { - return false - } - return code.IsOfType(pass, arg.Type, "*testing.M") -} - -func CheckExec(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - call := node.(*ast.CallExpr) - if !code.IsCallTo(pass, call, "os/exec.Command") { - return - } - val, ok := code.ExprToString(pass, call.Args[knowledge.Arg("os/exec.Command.name")]) - if !ok { - return - } - if !strings.Contains(val, " ") || strings.Contains(val, `\`) || strings.Contains(val, "/") { - return - } - report.Report(pass, call.Args[knowledge.Arg("os/exec.Command.name")], - "first argument to exec.Command looks like a shell command, but a program name or path are expected") - } - code.Preorder(pass, fn, (*ast.CallExpr)(nil)) - return nil, nil -} - -func CheckLoopEmptyDefault(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - loop := node.(*ast.ForStmt) - if len(loop.Body.List) != 1 || loop.Cond != nil || loop.Init != nil { - return - } - sel, ok := loop.Body.List[0].(*ast.SelectStmt) - if !ok { - return - } - for _, c := range sel.Body.List { - // FIXME this leaves behind an empty line, and possibly - // comments in the default branch. We can't easily fix - // either. - if comm, ok := c.(*ast.CommClause); ok && comm.Comm == nil && len(comm.Body) == 0 { - report.Report(pass, comm, "should not have an empty default case in a for+select loop; the loop will spin", - report.Fixes(edit.Fix("remove empty default branch", edit.Delete(comm)))) - // there can only be one default case - break - } - } - } - code.Preorder(pass, fn, (*ast.ForStmt)(nil)) - return nil, nil -} - -func CheckLhsRhsIdentical(pass *analysis.Pass) (interface{}, error) { - var isFloat func(T types.Type) bool - isFloat = func(T types.Type) bool { - tset := typeutil.NewTypeSet(T) - if len(tset.Terms) == 0 { - // no terms, so floats are a possibility - return true - } - return tset.Any(func(term *types.Term) bool { - switch typ := term.Type().Underlying().(type) { - case *types.Basic: - kind := typ.Kind() - return kind == types.Float32 || kind == types.Float64 - case *types.Array: - return isFloat(typ.Elem()) - case *types.Struct: - for i := 0; i < typ.NumFields(); i++ { - if !isFloat(typ.Field(i).Type()) { - return false - } - } - return true - default: - return false - } - }) - } - - // TODO(dh): this check ignores the existence of side-effects and - // happily flags fn() == fn() – so far, we've had nobody complain - // about a false positive, and it's caught several bugs in real - // code. - // - // We special case functions from the math/rand package. Someone ran - // into the following false positive: "rand.Intn(2) - rand.Intn(2), which I wrote to generate values {-1, 0, 1} with {0.25, 0.5, 0.25} probability." - fn := func(node ast.Node) { - op := node.(*ast.BinaryExpr) - switch op.Op { - case token.EQL, token.NEQ: - case token.SUB, token.QUO, token.AND, token.REM, token.OR, token.XOR, token.AND_NOT, - token.LAND, token.LOR, token.LSS, token.GTR, token.LEQ, token.GEQ: - default: - // For some ops, such as + and *, it can make sense to - // have identical operands - return - } - - if isFloat(pass.TypesInfo.TypeOf(op.X)) { - // 'float float' makes sense for several operators. - // We've tried keeping an exact list of operators to allow, but floats keep surprising us. Let's just give up instead. - return - } - - if reflect.TypeOf(op.X) != reflect.TypeOf(op.Y) { - return - } - if report.Render(pass, op.X) != report.Render(pass, op.Y) { - return - } - l1, ok1 := op.X.(*ast.BasicLit) - l2, ok2 := op.Y.(*ast.BasicLit) - if ok1 && ok2 && l1.Kind == token.INT && l2.Kind == l1.Kind && l1.Value == "0" && l2.Value == l1.Value && code.IsGenerated(pass, l1.Pos()) { - // cgo generates the following function call: - // _cgoCheckPointer(_cgoBase0, 0 == 0) – it uses 0 == 0 - // instead of true in case the user shadowed the - // identifier. Ideally we'd restrict this exception to - // calls of _cgoCheckPointer, but it's not worth the - // hassle of keeping track of the stack. - // are very rare to begin with, and we're mostly checking - // for them to catch typos such as 1 == 1 where the user - // meant to type i == 1. The odds of a false negative for - // 0 == 0 are slim. - return - } - - if expr, ok := op.X.(*ast.CallExpr); ok { - call := code.CallName(pass, expr) - switch call { - case "math/rand.Int", - "math/rand.Int31", - "math/rand.Int31n", - "math/rand.Int63", - "math/rand.Int63n", - "math/rand.Intn", - "math/rand.Uint32", - "math/rand.Uint64", - "math/rand.ExpFloat64", - "math/rand.Float32", - "math/rand.Float64", - "math/rand.NormFloat64", - "(*math/rand.Rand).Int", - "(*math/rand.Rand).Int31", - "(*math/rand.Rand).Int31n", - "(*math/rand.Rand).Int63", - "(*math/rand.Rand).Int63n", - "(*math/rand.Rand).Intn", - "(*math/rand.Rand).Uint32", - "(*math/rand.Rand).Uint64", - "(*math/rand.Rand).ExpFloat64", - "(*math/rand.Rand).Float32", - "(*math/rand.Rand).Float64", - "(*math/rand.Rand).NormFloat64": - return - } - } - - report.Report(pass, op, fmt.Sprintf("identical expressions on the left and right side of the '%s' operator", op.Op)) - } - code.Preorder(pass, fn, (*ast.BinaryExpr)(nil)) - return nil, nil -} - -func CheckScopedBreak(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - var body *ast.BlockStmt - switch node := node.(type) { - case *ast.ForStmt: - body = node.Body - case *ast.RangeStmt: - body = node.Body - default: - lint.ExhaustiveTypeSwitch(node) - } - for _, stmt := range body.List { - var blocks [][]ast.Stmt - switch stmt := stmt.(type) { - case *ast.SwitchStmt: - for _, c := range stmt.Body.List { - blocks = append(blocks, c.(*ast.CaseClause).Body) - } - case *ast.SelectStmt: - for _, c := range stmt.Body.List { - blocks = append(blocks, c.(*ast.CommClause).Body) - } - default: - continue - } - - for _, body := range blocks { - if len(body) == 0 { - continue - } - lasts := []ast.Stmt{body[len(body)-1]} - // TODO(dh): unfold all levels of nested block - // statements, not just a single level if statement - if ifs, ok := lasts[0].(*ast.IfStmt); ok { - if len(ifs.Body.List) == 0 { - continue - } - lasts[0] = ifs.Body.List[len(ifs.Body.List)-1] - - if block, ok := ifs.Else.(*ast.BlockStmt); ok { - if len(block.List) != 0 { - lasts = append(lasts, block.List[len(block.List)-1]) - } - } - } - for _, last := range lasts { - branch, ok := last.(*ast.BranchStmt) - if !ok || branch.Tok != token.BREAK || branch.Label != nil { - continue - } - report.Report(pass, branch, "ineffective break statement. Did you mean to break out of the outer loop?") - } - } - } - } - code.Preorder(pass, fn, (*ast.ForStmt)(nil), (*ast.RangeStmt)(nil)) - return nil, nil -} - -func CheckUnsafePrintf(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - call := node.(*ast.CallExpr) - name := code.CallName(pass, call) - var arg int - - switch name { - case "fmt.Printf", "fmt.Sprintf", "log.Printf": - arg = knowledge.Arg("fmt.Printf.format") - case "fmt.Fprintf": - arg = knowledge.Arg("fmt.Fprintf.format") - default: - return - } - if len(call.Args) != arg+1 { - return - } - switch call.Args[arg].(type) { - case *ast.CallExpr, *ast.Ident: - default: - return - } - - if _, ok := pass.TypesInfo.TypeOf(call.Args[arg]).(*types.Tuple); ok { - // the called function returns multiple values and got - // splatted into the call. for all we know, it is - // returning good arguments. - return - } - - alt := name[:len(name)-1] - report.Report(pass, call, - "printf-style function with dynamic format string and no further arguments should use print-style function instead", - report.Fixes(edit.Fix(fmt.Sprintf("use %s instead of %s", alt, name), edit.ReplaceWithString(call.Fun, alt)))) - } - code.Preorder(pass, fn, (*ast.CallExpr)(nil)) - return nil, nil -} - -func CheckEarlyDefer(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - block := node.(*ast.BlockStmt) - if len(block.List) < 2 { - return - } - for i, stmt := range block.List { - if i == len(block.List)-1 { - break - } - assign, ok := stmt.(*ast.AssignStmt) - if !ok { - continue - } - if len(assign.Rhs) != 1 { - continue - } - if len(assign.Lhs) < 2 { - continue - } - if lhs, ok := assign.Lhs[len(assign.Lhs)-1].(*ast.Ident); ok && lhs.Name == "_" { - continue - } - call, ok := assign.Rhs[0].(*ast.CallExpr) - if !ok { - continue - } - sig, ok := pass.TypesInfo.TypeOf(call.Fun).(*types.Signature) - if !ok { - continue - } - if sig.Results().Len() < 2 { - continue - } - last := sig.Results().At(sig.Results().Len() - 1) - // FIXME(dh): check that it's error from universe, not - // another type of the same name - if last.Type().String() != "error" { - continue - } - lhs, ok := assign.Lhs[0].(*ast.Ident) - if !ok { - continue - } - def, ok := block.List[i+1].(*ast.DeferStmt) - if !ok { - continue - } - sel, ok := def.Call.Fun.(*ast.SelectorExpr) - if !ok { - continue - } - ident, ok := selectorX(sel).(*ast.Ident) - if !ok { - continue - } - if pass.TypesInfo.ObjectOf(ident) != pass.TypesInfo.ObjectOf(lhs) { - continue - } - if sel.Sel.Name != "Close" { - continue - } - report.Report(pass, def, fmt.Sprintf("should check returned error before deferring %s", report.Render(pass, def.Call))) - } - } - code.Preorder(pass, fn, (*ast.BlockStmt)(nil)) - return nil, nil -} - -func selectorX(sel *ast.SelectorExpr) ast.Node { - switch x := sel.X.(type) { - case *ast.SelectorExpr: - return selectorX(x) - default: - return x - } -} - -func CheckEmptyCriticalSection(pass *analysis.Pass) (interface{}, error) { - if pass.Pkg.Path() == "sync_test" { - // exception for the sync package's tests - return nil, nil - } - - // Initially it might seem like this check would be easier to - // implement using IR. After all, we're only checking for two - // consecutive method calls. In reality, however, there may be any - // number of other instructions between the lock and unlock, while - // still constituting an empty critical section. For example, - // given `m.x().Lock(); m.x().Unlock()`, there will be a call to - // x(). In the AST-based approach, this has a tiny potential for a - // false positive (the second call to x might be doing work that - // is protected by the mutex). In an IR-based approach, however, - // it would miss a lot of real bugs. - - mutexParams := func(s ast.Stmt) (x ast.Expr, funcName string, ok bool) { - expr, ok := s.(*ast.ExprStmt) - if !ok { - return nil, "", false - } - call, ok := astutil.Unparen(expr.X).(*ast.CallExpr) - if !ok { - return nil, "", false - } - sel, ok := call.Fun.(*ast.SelectorExpr) - if !ok { - return nil, "", false - } - - fn, ok := pass.TypesInfo.ObjectOf(sel.Sel).(*types.Func) - if !ok { - return nil, "", false - } - sig := fn.Type().(*types.Signature) - if sig.Params().Len() != 0 || sig.Results().Len() != 0 { - return nil, "", false - } - - return sel.X, fn.Name(), true - } - - fn := func(node ast.Node) { - block := node.(*ast.BlockStmt) - if len(block.List) < 2 { - return - } - for i := range block.List[:len(block.List)-1] { - sel1, method1, ok1 := mutexParams(block.List[i]) - sel2, method2, ok2 := mutexParams(block.List[i+1]) - - if !ok1 || !ok2 || report.Render(pass, sel1) != report.Render(pass, sel2) { - continue - } - if (method1 == "Lock" && method2 == "Unlock") || - (method1 == "RLock" && method2 == "RUnlock") { - report.Report(pass, block.List[i+1], "empty critical section") - } - } - } - code.Preorder(pass, fn, (*ast.BlockStmt)(nil)) - return nil, nil -} - -var ( - // cgo produces code like fn(&*_Cvar_kSomeCallbacks) which we don't - // want to flag. - cgoIdent = regexp.MustCompile(`^_C(func|var)_.+$`) - checkIneffectiveCopyQ1 = pattern.MustParse(`(UnaryExpr "&" (StarExpr obj))`) - checkIneffectiveCopyQ2 = pattern.MustParse(`(StarExpr (UnaryExpr "&" _))`) -) - -func CheckIneffectiveCopy(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - if m, ok := code.Match(pass, checkIneffectiveCopyQ1, node); ok { - if ident, ok := m.State["obj"].(*ast.Ident); !ok || !cgoIdent.MatchString(ident.Name) { - report.Report(pass, node, "&*x will be simplified to x. It will not copy x.") - } - } else if _, ok := code.Match(pass, checkIneffectiveCopyQ2, node); ok { - report.Report(pass, node, "*&x will be simplified to x. It will not copy x.") - } - } - code.Preorder(pass, fn, (*ast.UnaryExpr)(nil), (*ast.StarExpr)(nil)) - return nil, nil -} - -func CheckCanonicalHeaderKey(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node, push bool) bool { - if !push { - return false - } - if assign, ok := node.(*ast.AssignStmt); ok { - // TODO(dh): This risks missing some Header reads, for - // example in `h1["foo"] = h2["foo"]` – these edge - // cases are probably rare enough to ignore for now. - for _, expr := range assign.Lhs { - op, ok := expr.(*ast.IndexExpr) - if !ok { - continue - } - if code.IsOfType(pass, op.X, "net/http.Header") { - return false - } - } - return true - } - op, ok := node.(*ast.IndexExpr) - if !ok { - return true - } - if !code.IsOfType(pass, op.X, "net/http.Header") { - return true - } - s, ok := code.ExprToString(pass, op.Index) - if !ok { - return true - } - canonical := http.CanonicalHeaderKey(s) - if s == canonical { - return true - } - var fix analysis.SuggestedFix - switch op.Index.(type) { - case *ast.BasicLit: - fix = edit.Fix("canonicalize header key", edit.ReplaceWithString(op.Index, strconv.Quote(canonical))) - case *ast.Ident: - call := &ast.CallExpr{ - Fun: edit.Selector("http", "CanonicalHeaderKey"), - Args: []ast.Expr{op.Index}, - } - fix = edit.Fix("wrap in http.CanonicalHeaderKey", edit.ReplaceWithNode(pass.Fset, op.Index, call)) - } - msg := fmt.Sprintf("keys in http.Header are canonicalized, %q is not canonical; fix the constant or use http.CanonicalHeaderKey", s) - if fix.Message != "" { - report.Report(pass, op, msg, report.Fixes(fix)) - } else { - report.Report(pass, op, msg) - } - return true - } - pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Nodes([]ast.Node{(*ast.AssignStmt)(nil), (*ast.IndexExpr)(nil)}, fn) - return nil, nil -} - -func CheckBenchmarkN(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - assign := node.(*ast.AssignStmt) - if len(assign.Lhs) != 1 || len(assign.Rhs) != 1 { - return - } - sel, ok := assign.Lhs[0].(*ast.SelectorExpr) - if !ok { - return - } - if sel.Sel.Name != "N" { - return - } - if !code.IsOfType(pass, sel.X, "*testing.B") { - return - } - report.Report(pass, assign, fmt.Sprintf("should not assign to %s", report.Render(pass, sel))) - } - code.Preorder(pass, fn, (*ast.AssignStmt)(nil)) - return nil, nil -} - -func CheckUnreadVariableValues(pass *analysis.Pass) (interface{}, error) { - for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { - if irutil.IsExample(fn) { - continue - } - node := fn.Source() - if node == nil { - continue - } - if gen, ok := code.Generator(pass, node.Pos()); ok && gen == generated.Goyacc { - // Don't flag unused values in code generated by goyacc. - // There may be hundreds of those due to the way the state - // machine is constructed. - continue - } - - switchTags := map[ir.Value]struct{}{} - ast.Inspect(node, func(node ast.Node) bool { - s, ok := node.(*ast.SwitchStmt) - if !ok { - return true - } - v, _ := fn.ValueForExpr(s.Tag) - switchTags[v] = struct{}{} - return true - }) - - // OPT(dh): don't use a map, possibly use a bitset - var hasUse func(v ir.Value, seen map[ir.Value]struct{}) bool - hasUse = func(v ir.Value, seen map[ir.Value]struct{}) bool { - if _, ok := seen[v]; ok { - return false - } - if _, ok := switchTags[v]; ok { - return true - } - refs := v.Referrers() - if refs == nil { - // TODO investigate why refs can be nil - return true - } - for _, ref := range *refs { - switch ref := ref.(type) { - case *ir.DebugRef: - case *ir.Sigma: - if seen == nil { - seen = map[ir.Value]struct{}{} - } - seen[v] = struct{}{} - if hasUse(ref, seen) { - return true - } - case *ir.Phi: - if seen == nil { - seen = map[ir.Value]struct{}{} - } - seen[v] = struct{}{} - if hasUse(ref, seen) { - return true - } - default: - return true - } - } - return false - } - - ast.Inspect(node, func(node ast.Node) bool { - assign, ok := node.(*ast.AssignStmt) - if !ok { - return true - } - if len(assign.Lhs) > 1 && len(assign.Rhs) == 1 { - // Either a function call with multiple return values, - // or a comma-ok assignment - - val, _ := fn.ValueForExpr(assign.Rhs[0]) - if val == nil { - return true - } - refs := val.Referrers() - if refs == nil { - return true - } - for _, ref := range *refs { - ex, ok := ref.(*ir.Extract) - if !ok { - continue - } - if !hasUse(ex, nil) { - lhs := assign.Lhs[ex.Index] - if ident, ok := lhs.(*ast.Ident); !ok || ok && ident.Name == "_" { - continue - } - report.Report(pass, assign, fmt.Sprintf("this value of %s is never used", lhs)) - } - } - return true - } - for i, lhs := range assign.Lhs { - rhs := assign.Rhs[i] - if ident, ok := lhs.(*ast.Ident); !ok || ok && ident.Name == "_" { - continue - } - val, _ := fn.ValueForExpr(rhs) - if val == nil { - continue - } - - if _, ok := val.(*ir.Const); ok { - // a zero-valued constant, for example in 'foo := []string(nil)' - continue - } - if !hasUse(val, nil) { - report.Report(pass, assign, fmt.Sprintf("this value of %s is never used", lhs)) - } - } - return true - }) - } - return nil, nil -} - -func CheckPredeterminedBooleanExprs(pass *analysis.Pass) (interface{}, error) { - for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { - for _, block := range fn.Blocks { - for _, ins := range block.Instrs { - binop, ok := ins.(*ir.BinOp) - if !ok { - continue - } - switch binop.Op { - case token.GTR, token.LSS, token.EQL, token.NEQ, token.LEQ, token.GEQ: - default: - continue - } - - xs, ok1 := consts(binop.X, nil, nil) - ys, ok2 := consts(binop.Y, nil, nil) - if !ok1 || !ok2 || len(xs) == 0 || len(ys) == 0 { - continue - } - - trues := 0 - for _, x := range xs { - for _, y := range ys { - if x.Value == nil { - if y.Value == nil { - trues++ - } - continue - } - if constant.Compare(x.Value, binop.Op, y.Value) { - trues++ - } - } - } - b := trues != 0 - if trues == 0 || trues == len(xs)*len(ys) { - report.Report(pass, binop, fmt.Sprintf("binary expression is always %t for all possible values (%s %s %s)", b, xs, binop.Op, ys)) - } - } - } - } - return nil, nil -} - -func CheckNilMaps(pass *analysis.Pass) (interface{}, error) { - for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { - for _, block := range fn.Blocks { - for _, ins := range block.Instrs { - mu, ok := ins.(*ir.MapUpdate) - if !ok { - continue - } - c, ok := irutil.Flatten(mu.Map).(*ir.Const) - if !ok { - continue - } - if c.Value != nil { - continue - } - report.Report(pass, mu, "assignment to nil map") - } - } - } - return nil, nil -} - -func CheckExtremeComparison(pass *analysis.Pass) (interface{}, error) { - isobj := func(expr ast.Expr, name string) bool { - sel, ok := expr.(*ast.SelectorExpr) - if !ok { - return false - } - return typeutil.IsObject(pass.TypesInfo.ObjectOf(sel.Sel), name) - } - - fn := func(node ast.Node) { - expr := node.(*ast.BinaryExpr) - tx := pass.TypesInfo.TypeOf(expr.X) - basic, ok := tx.Underlying().(*types.Basic) - if !ok { - return - } - - var max string - var min string - - switch basic.Kind() { - case types.Uint8: - max = "math.MaxUint8" - case types.Uint16: - max = "math.MaxUint16" - case types.Uint32: - max = "math.MaxUint32" - case types.Uint64: - max = "math.MaxUint64" - case types.Uint: - max = "math.MaxUint64" - - case types.Int8: - min = "math.MinInt8" - max = "math.MaxInt8" - case types.Int16: - min = "math.MinInt16" - max = "math.MaxInt16" - case types.Int32: - min = "math.MinInt32" - max = "math.MaxInt32" - case types.Int64: - min = "math.MinInt64" - max = "math.MaxInt64" - case types.Int: - min = "math.MinInt64" - max = "math.MaxInt64" - } - - if (expr.Op == token.GTR || expr.Op == token.GEQ) && isobj(expr.Y, max) || - (expr.Op == token.LSS || expr.Op == token.LEQ) && isobj(expr.X, max) { - report.Report(pass, expr, fmt.Sprintf("no value of type %s is greater than %s", basic, max)) - } - if expr.Op == token.LEQ && isobj(expr.Y, max) || - expr.Op == token.GEQ && isobj(expr.X, max) { - report.Report(pass, expr, fmt.Sprintf("every value of type %s is <= %s", basic, max)) - } - - isZeroLiteral := func(expr ast.Expr) bool { - return code.IsIntegerLiteral(pass, expr, constant.MakeInt64(0)) - } - if (basic.Info() & types.IsUnsigned) != 0 { - if (expr.Op == token.LSS && isZeroLiteral(expr.Y)) || - (expr.Op == token.GTR && isZeroLiteral(expr.X)) { - report.Report(pass, expr, fmt.Sprintf("no value of type %s is less than 0", basic)) - } - if expr.Op == token.GEQ && isZeroLiteral(expr.Y) || - expr.Op == token.LEQ && isZeroLiteral(expr.X) { - report.Report(pass, expr, fmt.Sprintf("every value of type %s is >= 0", basic)) - } - } else { - if (expr.Op == token.LSS || expr.Op == token.LEQ) && isobj(expr.Y, min) || - (expr.Op == token.GTR || expr.Op == token.GEQ) && isobj(expr.X, min) { - report.Report(pass, expr, fmt.Sprintf("no value of type %s is less than %s", basic, min)) - } - if expr.Op == token.GEQ && isobj(expr.Y, min) || - expr.Op == token.LEQ && isobj(expr.X, min) { - report.Report(pass, expr, fmt.Sprintf("every value of type %s is >= %s", basic, min)) - } - } - - } - code.Preorder(pass, fn, (*ast.BinaryExpr)(nil)) - return nil, nil -} - -func consts(val ir.Value, out []*ir.Const, visitedPhis map[string]bool) ([]*ir.Const, bool) { - if visitedPhis == nil { - visitedPhis = map[string]bool{} - } - var ok bool - switch val := val.(type) { - case *ir.Phi: - if visitedPhis[val.Name()] { - break - } - visitedPhis[val.Name()] = true - vals := val.Operands(nil) - for _, phival := range vals { - out, ok = consts(*phival, out, visitedPhis) - if !ok { - return nil, false - } - } - case *ir.Const: - out = append(out, val) - case *ir.Convert: - out, ok = consts(val.X, out, visitedPhis) - if !ok { - return nil, false - } - default: - return nil, false - } - if len(out) < 2 { - return out, true - } - uniq := []*ir.Const{out[0]} - for _, val := range out[1:] { - if val.Value == uniq[len(uniq)-1].Value { - continue - } - uniq = append(uniq, val) - } - return uniq, true -} - -func CheckLoopCondition(pass *analysis.Pass) (interface{}, error) { - for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { - cb := func(node ast.Node) bool { - loop, ok := node.(*ast.ForStmt) - if !ok { - return true - } - if loop.Init == nil || loop.Cond == nil || loop.Post == nil { - return true - } - init, ok := loop.Init.(*ast.AssignStmt) - if !ok || len(init.Lhs) != 1 || len(init.Rhs) != 1 { - return true - } - cond, ok := loop.Cond.(*ast.BinaryExpr) - if !ok { - return true - } - x, ok := cond.X.(*ast.Ident) - if !ok { - return true - } - lhs, ok := init.Lhs[0].(*ast.Ident) - if !ok { - return true - } - if pass.TypesInfo.ObjectOf(x) != pass.TypesInfo.ObjectOf(lhs) { - return true - } - if _, ok := loop.Post.(*ast.IncDecStmt); !ok { - return true - } - - v, isAddr := fn.ValueForExpr(cond.X) - if v == nil || isAddr { - return true - } - switch v := v.(type) { - case *ir.Phi: - ops := v.Operands(nil) - if len(ops) != 2 { - return true - } - _, ok := (*ops[0]).(*ir.Const) - if !ok { - return true - } - sigma, ok := (*ops[1]).(*ir.Sigma) - if !ok { - return true - } - if sigma.X != v { - return true - } - case *ir.Load: - return true - } - report.Report(pass, cond, "variable in loop condition never changes") - - return true - } - if source := fn.Source(); source != nil { - ast.Inspect(source, cb) - } - } - return nil, nil -} - -func CheckArgOverwritten(pass *analysis.Pass) (interface{}, error) { - for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { - cb := func(node ast.Node) bool { - var typ *ast.FuncType - var body *ast.BlockStmt - switch fn := node.(type) { - case *ast.FuncDecl: - typ = fn.Type - body = fn.Body - case *ast.FuncLit: - typ = fn.Type - body = fn.Body - } - if body == nil { - return true - } - if len(typ.Params.List) == 0 { - return true - } - for _, field := range typ.Params.List { - for _, arg := range field.Names { - obj := pass.TypesInfo.ObjectOf(arg) - var irobj *ir.Parameter - for _, param := range fn.Params { - if param.Object() == obj { - irobj = param - break - } - } - if irobj == nil { - continue - } - refs := irobj.Referrers() - if refs == nil { - continue - } - if len(irutil.FilterDebug(*refs)) != 0 { - continue - } - - var assignment ast.Node - ast.Inspect(body, func(node ast.Node) bool { - if assignment != nil { - return false - } - assign, ok := node.(*ast.AssignStmt) - if !ok { - return true - } - for _, lhs := range assign.Lhs { - ident, ok := lhs.(*ast.Ident) - if !ok { - continue - } - if pass.TypesInfo.ObjectOf(ident) == obj { - assignment = assign - return false - } - } - return true - }) - if assignment != nil { - report.Report(pass, arg, fmt.Sprintf("argument %s is overwritten before first use", arg), - report.Related(assignment, fmt.Sprintf("assignment to %s", arg))) - } - } - } - return true - } - if source := fn.Source(); source != nil { - ast.Inspect(source, cb) - } - } - return nil, nil -} - -func CheckIneffectiveLoop(pass *analysis.Pass) (interface{}, error) { - // This check detects some, but not all unconditional loop exits. - // We give up in the following cases: - // - // - a goto anywhere in the loop. The goto might skip over our - // return, and we don't check that it doesn't. - // - // - any nested, unlabelled continue, even if it is in another - // loop or closure. - fn := func(node ast.Node) { - var body *ast.BlockStmt - switch fn := node.(type) { - case *ast.FuncDecl: - body = fn.Body - case *ast.FuncLit: - body = fn.Body - default: - lint.ExhaustiveTypeSwitch(node) - } - if body == nil { - return - } - labels := map[types.Object]ast.Stmt{} - ast.Inspect(body, func(node ast.Node) bool { - label, ok := node.(*ast.LabeledStmt) - if !ok { - return true - } - labels[pass.TypesInfo.ObjectOf(label.Label)] = label.Stmt - return true - }) - - ast.Inspect(body, func(node ast.Node) bool { - var loop ast.Node - var body *ast.BlockStmt - switch node := node.(type) { - case *ast.ForStmt: - body = node.Body - loop = node - case *ast.RangeStmt: - ok := typeutil.All(pass.TypesInfo.TypeOf(node.X), func(term *types.Term) bool { - switch term.Type().Underlying().(type) { - case *types.Slice, *types.Chan, *types.Basic, *types.Pointer, *types.Array: - return true - case *types.Map: - // looping once over a map is a valid pattern for - // getting an arbitrary element. - return false - default: - lint.ExhaustiveTypeSwitch(term.Type().Underlying()) - return false - } - }) - if !ok { - return true - } - body = node.Body - loop = node - default: - return true - } - if len(body.List) < 2 { - // TODO(dh): is this check needed? when body.List < 2, - // then we can't find both an unconditional exit and a - // branching statement (if, ...). and we don't flag - // unconditional exits if there has been no branching - // in the loop body. - - // avoid flagging the somewhat common pattern of using - // a range loop to get the first element in a slice, - // or the first rune in a string. - return true - } - var unconditionalExit ast.Node - hasBranching := false - for _, stmt := range body.List { - switch stmt := stmt.(type) { - case *ast.BranchStmt: - switch stmt.Tok { - case token.BREAK: - if stmt.Label == nil || labels[pass.TypesInfo.ObjectOf(stmt.Label)] == loop { - unconditionalExit = stmt - } - case token.CONTINUE: - if stmt.Label == nil || labels[pass.TypesInfo.ObjectOf(stmt.Label)] == loop { - unconditionalExit = nil - return false - } - } - case *ast.ReturnStmt: - unconditionalExit = stmt - case *ast.IfStmt, *ast.ForStmt, *ast.RangeStmt, *ast.SwitchStmt, *ast.SelectStmt: - hasBranching = true - } - } - if unconditionalExit == nil || !hasBranching { - return false - } - ast.Inspect(body, func(node ast.Node) bool { - if branch, ok := node.(*ast.BranchStmt); ok { - - switch branch.Tok { - case token.GOTO: - unconditionalExit = nil - return false - case token.CONTINUE: - if branch.Label != nil && labels[pass.TypesInfo.ObjectOf(branch.Label)] != loop { - return true - } - unconditionalExit = nil - return false - } - } - return true - }) - if unconditionalExit != nil { - report.Report(pass, unconditionalExit, "the surrounding loop is unconditionally terminated") - } - return true - }) - } - code.Preorder(pass, fn, (*ast.FuncDecl)(nil), (*ast.FuncLit)(nil)) - return nil, nil -} - -var checkNilContextQ = pattern.MustParse(`(CallExpr fun@(Symbol _) (Builtin "nil"):_)`) - -func CheckNilContext(pass *analysis.Pass) (interface{}, error) { - todo := &ast.CallExpr{ - Fun: edit.Selector("context", "TODO"), - } - bg := &ast.CallExpr{ - Fun: edit.Selector("context", "Background"), - } - fn := func(node ast.Node) { - m, ok := code.Match(pass, checkNilContextQ, node) - if !ok { - return - } - - call := node.(*ast.CallExpr) - fun, ok := m.State["fun"].(*types.Func) - if !ok { - // it might also be a builtin - return - } - sig := fun.Type().(*types.Signature) - if sig.Params().Len() == 0 { - // Our CallExpr might've matched a method expression, like - // (*T).Foo(nil) – here, nil isn't the first argument of - // the Foo method, but the method receiver. - return - } - if !typeutil.IsType(sig.Params().At(0).Type(), "context.Context") { - return - } - report.Report(pass, call.Args[0], - "do not pass a nil Context, even if a function permits it; pass context.TODO if you are unsure about which Context to use", report.Fixes( - edit.Fix("use context.TODO", edit.ReplaceWithNode(pass.Fset, call.Args[0], todo)), - edit.Fix("use context.Background", edit.ReplaceWithNode(pass.Fset, call.Args[0], bg)))) - } - code.Preorder(pass, fn, (*ast.CallExpr)(nil)) - return nil, nil -} - -var ( - checkSeekerQ = pattern.MustParse(`(CallExpr fun@(SelectorExpr _ (Ident "Seek")) [arg1@(SelectorExpr _ (Symbol (Or "io.SeekStart" "io.SeekCurrent" "io.SeekEnd"))) arg2])`) - checkSeekerR = pattern.MustParse(`(CallExpr fun [arg2 arg1])`) -) - -func CheckSeeker(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - if m, edits, ok := code.MatchAndEdit(pass, checkSeekerQ, checkSeekerR, node); ok { - if !code.IsMethod(pass, m.State["fun"].(*ast.SelectorExpr), "Seek", knowledge.Signatures["(io.Seeker).Seek"]) { - return - } - report.Report(pass, node, "the first argument of io.Seeker is the offset, but an io.Seek* constant is being used instead", - report.Fixes(edit.Fix("swap arguments", edits...))) - } - } - code.Preorder(pass, fn, (*ast.CallExpr)(nil)) - return nil, nil -} - -func CheckIneffectiveAppend(pass *analysis.Pass) (interface{}, error) { - isAppend := func(ins ir.Value) bool { - call, ok := ins.(*ir.Call) - if !ok { - return false - } - if call.Call.IsInvoke() { - return false - } - if builtin, ok := call.Call.Value.(*ir.Builtin); !ok || builtin.Name() != "append" { - return false - } - return true - } - - // We have to be careful about aliasing. - // Multiple slices may refer to the same backing array, - // making appends observable even when we don't see the result of append be used anywhere. - // - // We will have to restrict ourselves to slices that have been allocated within the function, - // haven't been sliced, - // and haven't been passed anywhere that could retain them (such as function calls or memory stores). - // - // We check whether an append should be flagged in two steps. - // - // In the first step, we look at the data flow graph, starting in reverse from the argument to append, till we reach the root. - // This graph must only consist of the following instructions: - // - // - phi - // - sigma - // - slice - // - const nil - // - MakeSlice - // - Alloc - // - calls to append - // - // If this step succeeds, we look at all referrers of the values found in the first step, recursively. - // These referrers must either be in the set of values found in the first step, - // be DebugRefs, - // or fulfill the same type requirements as step 1, with the exception of appends, which are forbidden. - // - // If both steps succeed then we know that the backing array hasn't been aliased in an observable manner. - // - // We could relax these restrictions by making use of additional information: - // - if we passed the slice to a function that doesn't retain the slice then we can still flag it - // - if a slice has been sliced but is dead afterwards, we can flag appends to the new slice - - // OPT(dh): We could cache the results of both validate functions. - // However, we only use these functions on values that we otherwise want to flag, which are very few. - // Not caching values hasn't increased the runtimes for the standard library nor k8s. - var validateArgument func(v ir.Value, seen map[ir.Value]struct{}) bool - validateArgument = func(v ir.Value, seen map[ir.Value]struct{}) bool { - if _, ok := seen[v]; ok { - // break cycle - return true - } - seen[v] = struct{}{} - switch v := v.(type) { - case *ir.Phi: - for _, edge := range v.Edges { - if !validateArgument(edge, seen) { - return false - } - } - return true - case *ir.Sigma: - return validateArgument(v.X, seen) - case *ir.Slice: - return validateArgument(v.X, seen) - case *ir.Const: - return true - case *ir.MakeSlice: - return true - case *ir.Alloc: - return true - case *ir.Call: - if isAppend(v) { - return validateArgument(v.Call.Args[0], seen) - } - return false - default: - return false - } - } - - var validateReferrers func(v ir.Value, seen map[ir.Instruction]struct{}) bool - validateReferrers = func(v ir.Value, seen map[ir.Instruction]struct{}) bool { - for _, ref := range *v.Referrers() { - if _, ok := seen[ref]; ok { - continue - } - - seen[ref] = struct{}{} - switch ref.(type) { - case *ir.Phi: - case *ir.Sigma: - case *ir.Slice: - case *ir.Const: - case *ir.MakeSlice: - case *ir.Alloc: - case *ir.DebugRef: - default: - return false - } - - if ref, ok := ref.(ir.Value); ok { - if !validateReferrers(ref, seen) { - return false - } - } - } - return true - } - - for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { - for _, block := range fn.Blocks { - for _, ins := range block.Instrs { - val, ok := ins.(ir.Value) - if !ok || !isAppend(val) { - continue - } - - isUsed := false - visited := map[ir.Instruction]bool{} - var walkRefs func(refs []ir.Instruction) - walkRefs = func(refs []ir.Instruction) { - loop: - for _, ref := range refs { - if visited[ref] { - continue - } - visited[ref] = true - if _, ok := ref.(*ir.DebugRef); ok { - continue - } - switch ref := ref.(type) { - case *ir.Phi: - walkRefs(*ref.Referrers()) - case *ir.Sigma: - walkRefs(*ref.Referrers()) - case ir.Value: - if !isAppend(ref) { - isUsed = true - } else { - walkRefs(*ref.Referrers()) - } - case ir.Instruction: - isUsed = true - break loop - } - } - } - - refs := val.Referrers() - if refs == nil { - continue - } - walkRefs(*refs) - - if isUsed { - continue - } - - seen := map[ir.Value]struct{}{} - if !validateArgument(ins.(*ir.Call).Call.Args[0], seen) { - continue - } - - seen2 := map[ir.Instruction]struct{}{} - for k := range seen { - // the only values we allow are also instructions, so this type assertion cannot fail - seen2[k.(ir.Instruction)] = struct{}{} - } - seen2[ins] = struct{}{} - failed := false - for v := range seen { - if !validateReferrers(v, seen2) { - failed = true - break - } - } - if !failed { - report.Report(pass, ins, "this result of append is never used, except maybe in other appends") - } - } - } - } - return nil, nil -} - -func CheckConcurrentTesting(pass *analysis.Pass) (interface{}, error) { - for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { - for _, block := range fn.Blocks { - for _, ins := range block.Instrs { - gostmt, ok := ins.(*ir.Go) - if !ok { - continue - } - var fn *ir.Function - switch val := gostmt.Call.Value.(type) { - case *ir.Function: - fn = val - case *ir.MakeClosure: - fn = val.Fn.(*ir.Function) - default: - continue - } - if fn.Blocks == nil { - continue - } - for _, block := range fn.Blocks { - for _, ins := range block.Instrs { - call, ok := ins.(*ir.Call) - if !ok { - continue - } - if call.Call.IsInvoke() { - continue - } - callee := call.Call.StaticCallee() - if callee == nil { - continue - } - recv := callee.Signature.Recv() - if recv == nil { - continue - } - if !typeutil.IsType(recv.Type(), "*testing.common") { - continue - } - fn, ok := call.Call.StaticCallee().Object().(*types.Func) - if !ok { - continue - } - name := fn.Name() - switch name { - case "FailNow", "Fatal", "Fatalf", "SkipNow", "Skip", "Skipf": - default: - continue - } - // TODO(dh): don't report multiple diagnostics - // for multiple calls to T.Fatal, but do - // collect all of them as related information - report.Report(pass, gostmt, fmt.Sprintf("the goroutine calls T.%s, which must be called in the same goroutine as the test", name), - report.Related(call, fmt.Sprintf("call to T.%s", name))) - } - } - } - } - } - return nil, nil -} - -func eachCall(fn *ir.Function, cb func(caller *ir.Function, site ir.CallInstruction, callee *ir.Function)) { - for _, b := range fn.Blocks { - for _, instr := range b.Instrs { - if site, ok := instr.(ir.CallInstruction); ok { - if g := site.Common().StaticCallee(); g != nil { - cb(fn, site, g) - } - } - } - } -} - -func CheckCyclicFinalizer(pass *analysis.Pass) (interface{}, error) { - cb := func(caller *ir.Function, site ir.CallInstruction, callee *ir.Function) { - if callee.RelString(nil) != "runtime.SetFinalizer" { - return - } - arg0 := site.Common().Args[knowledge.Arg("runtime.SetFinalizer.obj")] - if iface, ok := arg0.(*ir.MakeInterface); ok { - arg0 = iface.X - } - load, ok := arg0.(*ir.Load) - if !ok { - return - } - v, ok := load.X.(*ir.Alloc) - if !ok { - return - } - arg1 := site.Common().Args[knowledge.Arg("runtime.SetFinalizer.finalizer")] - if iface, ok := arg1.(*ir.MakeInterface); ok { - arg1 = iface.X - } - mc, ok := arg1.(*ir.MakeClosure) - if !ok { - return - } - for _, b := range mc.Bindings { - if b == v { - pos := report.DisplayPosition(pass.Fset, mc.Fn.Pos()) - report.Report(pass, site, fmt.Sprintf("the finalizer closes over the object, preventing the finalizer from ever running (at %s)", pos)) - } - } - } - for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { - eachCall(fn, cb) - } - return nil, nil -} - -/* -func CheckSliceOutOfBounds(pass *analysis.Pass) (interface{}, error) { - for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { - for _, block := range fn.Blocks { - for _, ins := range block.Instrs { - ia, ok := ins.(*ir.IndexAddr) - if !ok { - continue - } - if _, ok := ia.X.Type().Underlying().(*types.Slice); !ok { - continue - } - sr, ok1 := c.funcDescs.Get(fn).Ranges[ia.X].(vrp.SliceInterval) - idxr, ok2 := c.funcDescs.Get(fn).Ranges[ia.Index].(vrp.IntInterval) - if !ok1 || !ok2 || !sr.IsKnown() || !idxr.IsKnown() || sr.Length.Empty() || idxr.Empty() { - continue - } - if idxr.Lower.Cmp(sr.Length.Upper) >= 0 { - report.Nodef(pass, ia, "index out of bounds") - } - } - } - } - return nil, nil -} -*/ - -func CheckDeferLock(pass *analysis.Pass) (interface{}, error) { - for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { - for _, block := range fn.Blocks { - instrs := irutil.FilterDebug(block.Instrs) - if len(instrs) < 2 { - continue - } - for i, ins := range instrs[:len(instrs)-1] { - call, ok := ins.(*ir.Call) - if !ok { - continue - } - if !irutil.IsCallToAny(call.Common(), "(*sync.Mutex).Lock", "(*sync.RWMutex).RLock") { - continue - } - nins, ok := instrs[i+1].(*ir.Defer) - if !ok { - continue - } - if !irutil.IsCallToAny(&nins.Call, "(*sync.Mutex).Lock", "(*sync.RWMutex).RLock") { - continue - } - if call.Common().Args[0] != nins.Call.Args[0] { - continue - } - name := shortCallName(call.Common()) - alt := "" - switch name { - case "Lock": - alt = "Unlock" - case "RLock": - alt = "RUnlock" - } - report.Report(pass, nins, fmt.Sprintf("deferring %s right after having locked already; did you mean to defer %s?", name, alt)) - } - } - } - return nil, nil -} - -func CheckNaNComparison(pass *analysis.Pass) (interface{}, error) { - isNaN := func(v ir.Value) bool { - call, ok := v.(*ir.Call) - if !ok { - return false - } - return irutil.IsCallTo(call.Common(), "math.NaN") - } - for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { - for _, block := range fn.Blocks { - for _, ins := range block.Instrs { - ins, ok := ins.(*ir.BinOp) - if !ok { - continue - } - if isNaN(irutil.Flatten(ins.X)) || isNaN(irutil.Flatten(ins.Y)) { - report.Report(pass, ins, "no value is equal to NaN, not even NaN itself") - } - } - } - } - return nil, nil -} - -func CheckInfiniteRecursion(pass *analysis.Pass) (interface{}, error) { - for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { - eachCall(fn, func(caller *ir.Function, site ir.CallInstruction, callee *ir.Function) { - if callee != fn { - return - } - if _, ok := site.(*ir.Go); ok { - // Recursively spawning goroutines doesn't consume - // stack space infinitely, so don't flag it. - return - } - - block := site.Block() - for _, b := range fn.Blocks { - if block.Dominates(b) { - continue - } - if len(b.Instrs) == 0 { - continue - } - if _, ok := b.Control().(*ir.Return); ok { - return - } - } - report.Report(pass, site, "infinite recursive call") - }) - } - return nil, nil -} - -func CheckLeakyTimeTick(pass *analysis.Pass) (interface{}, error) { - for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { - if code.IsMainLike(pass) || code.IsInTest(pass, fn) { - continue - } - for _, block := range fn.Blocks { - for _, ins := range block.Instrs { - call, ok := ins.(*ir.Call) - if !ok || !irutil.IsCallTo(call.Common(), "time.Tick") { - continue - } - if !irutil.Terminates(call.Parent()) { - continue - } - report.Report(pass, call, "using time.Tick leaks the underlying ticker, consider using it only in endless functions, tests and the main package, and use time.NewTicker here") - } - } - } - return nil, nil -} - -var checkDoubleNegationQ = pattern.MustParse(`(UnaryExpr "!" single@(UnaryExpr "!" x))`) - -func CheckDoubleNegation(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - if m, ok := code.Match(pass, checkDoubleNegationQ, node); ok { - report.Report(pass, node, "negating a boolean twice has no effect; is this a typo?", report.Fixes( - edit.Fix("turn into single negation", edit.ReplaceWithNode(pass.Fset, node, m.State["single"].(ast.Node))), - edit.Fix("remove double negation", edit.ReplaceWithNode(pass.Fset, node, m.State["x"].(ast.Node))))) - } - } - code.Preorder(pass, fn, (*ast.UnaryExpr)(nil)) - return nil, nil -} - -func CheckRepeatedIfElse(pass *analysis.Pass) (interface{}, error) { - seen := map[ast.Node]bool{} - - var collectConds func(ifstmt *ast.IfStmt, conds []ast.Expr) ([]ast.Expr, bool) - collectConds = func(ifstmt *ast.IfStmt, conds []ast.Expr) ([]ast.Expr, bool) { - seen[ifstmt] = true - // Bail if any if-statement has an Init statement or side effects in its condition - if ifstmt.Init != nil { - return nil, false - } - if code.MayHaveSideEffects(pass, ifstmt.Cond, nil) { - return nil, false - } - - conds = append(conds, ifstmt.Cond) - if elsestmt, ok := ifstmt.Else.(*ast.IfStmt); ok { - return collectConds(elsestmt, conds) - } - return conds, true - } - fn := func(node ast.Node) { - ifstmt := node.(*ast.IfStmt) - if seen[ifstmt] { - // this if-statement is part of an if/else-if chain that we've already processed - return - } - if ifstmt.Else == nil { - // there can be at most one condition - return - } - conds, ok := collectConds(ifstmt, nil) - if !ok { - return - } - if len(conds) < 2 { - return - } - counts := map[string]int{} - for _, cond := range conds { - s := report.Render(pass, cond) - counts[s]++ - if counts[s] == 2 { - report.Report(pass, cond, "this condition occurs multiple times in this if/else if chain") - } - } - } - code.Preorder(pass, fn, (*ast.IfStmt)(nil)) - return nil, nil -} - -func CheckSillyBitwiseOps(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - binop := node.(*ast.BinaryExpr) - if !typeutil.All(pass.TypesInfo.TypeOf(binop), func(term *types.Term) bool { - b, ok := term.Type().Underlying().(*types.Basic) - if !ok { - return false - } - return (b.Info() & types.IsInteger) != 0 - }) { - return - } - switch binop.Op { - case token.AND, token.OR, token.XOR: - default: - // we do not flag shifts because too often, x<<0 is part - // of a pattern, x<<0, x<<8, x<<16, ... - return - } - if y, ok := binop.Y.(*ast.Ident); ok { - obj, ok := pass.TypesInfo.ObjectOf(y).(*types.Const) - if !ok { - return - } - if obj.Pkg() != pass.Pkg { - // identifier was dot-imported - return - } - if v, _ := constant.Int64Val(obj.Val()); v != 0 { - return - } - path, _ := astutil.PathEnclosingInterval(code.File(pass, obj), obj.Pos(), obj.Pos()) - if len(path) < 2 { - return - } - spec, ok := path[1].(*ast.ValueSpec) - if !ok { - return - } - if len(spec.Names) != 1 || len(spec.Values) != 1 { - // TODO(dh): we could support this - return - } - ident, ok := spec.Values[0].(*ast.Ident) - if !ok { - return - } - if !isIota(pass.TypesInfo.ObjectOf(ident)) { - return - } - switch binop.Op { - case token.AND: - report.Report(pass, node, - fmt.Sprintf("%s always equals 0; %s is defined as iota and has value 0, maybe %s is meant to be 1 << iota?", report.Render(pass, binop), report.Render(pass, binop.Y), report.Render(pass, binop.Y))) - case token.OR, token.XOR: - report.Report(pass, node, - fmt.Sprintf("%s always equals %s; %s is defined as iota and has value 0, maybe %s is meant to be 1 << iota?", report.Render(pass, binop), report.Render(pass, binop.X), report.Render(pass, binop.Y), report.Render(pass, binop.Y))) - } - } else if code.IsIntegerLiteral(pass, binop.Y, constant.MakeInt64(0)) { - switch binop.Op { - case token.AND: - report.Report(pass, node, fmt.Sprintf("%s always equals 0", report.Render(pass, binop))) - case token.OR, token.XOR: - report.Report(pass, node, fmt.Sprintf("%s always equals %s", report.Render(pass, binop), report.Render(pass, binop.X))) - } - } - } - code.Preorder(pass, fn, (*ast.BinaryExpr)(nil)) - return nil, nil -} - -func isIota(obj types.Object) bool { - if obj.Name() != "iota" { - return false - } - c, ok := obj.(*types.Const) - if !ok { - return false - } - return c.Pkg() == nil -} - -func CheckNonOctalFileMode(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - call := node.(*ast.CallExpr) - for _, arg := range call.Args { - lit, ok := arg.(*ast.BasicLit) - if !ok { - continue - } - if !typeutil.IsType(pass.TypesInfo.TypeOf(lit), "os.FileMode") && - !typeutil.IsType(pass.TypesInfo.TypeOf(lit), "io/fs.FileMode") { - continue - } - if len(lit.Value) == 3 && - lit.Value[0] != '0' && - lit.Value[0] >= '0' && lit.Value[0] <= '7' && - lit.Value[1] >= '0' && lit.Value[1] <= '7' && - lit.Value[2] >= '0' && lit.Value[2] <= '7' { - - v, err := strconv.ParseInt(lit.Value, 10, 64) - if err != nil { - continue - } - report.Report(pass, arg, fmt.Sprintf("file mode '%s' evaluates to %#o; did you mean '0%s'?", lit.Value, v, lit.Value), - report.Fixes(edit.Fix("fix octal literal", edit.ReplaceWithString(arg, "0"+lit.Value)))) - } - } - } - code.Preorder(pass, fn, (*ast.CallExpr)(nil)) - return nil, nil -} - -func CheckSideEffectFreeCalls(pass *analysis.Pass) (interface{}, error) { - pure := pass.ResultOf[purity.Analyzer].(purity.Result) - -fnLoop: - for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { - if code.IsInTest(pass, fn) { - params := fn.Signature.Params() - for i := 0; i < params.Len(); i++ { - param := params.At(i) - if typeutil.IsType(param.Type(), "*testing.B") { - // Ignore discarded pure functions in code related - // to benchmarks. Instead of matching BenchmarkFoo - // functions, we match any function accepting a - // *testing.B. Benchmarks sometimes call generic - // functions for doing the actual work, and - // checking for the parameter is a lot easier and - // faster than analyzing call trees. - continue fnLoop - } - } - } - - for _, b := range fn.Blocks { - for _, ins := range b.Instrs { - ins, ok := ins.(*ir.Call) - if !ok { - continue - } - refs := ins.Referrers() - if refs == nil || len(irutil.FilterDebug(*refs)) > 0 { - continue - } - - callee := ins.Common().StaticCallee() - if callee == nil { - continue - } - if callee.Object() == nil { - // TODO(dh): support anonymous functions - continue - } - if _, ok := pure[callee.Object().(*types.Func)]; ok { - if pass.Pkg.Path() == "fmt_test" && callee.Object().(*types.Func).FullName() == "fmt.Sprintf" { - // special case for benchmarks in the fmt package - continue - } - report.Report(pass, ins, fmt.Sprintf("%s doesn't have side effects and its return value is ignored", callee.Object().Name())) - } - } - } - } - return nil, nil -} - -func CheckDeprecated(pass *analysis.Pass) (interface{}, error) { - deprs := pass.ResultOf[deprecated.Analyzer].(deprecated.Result) - - // Selectors can appear outside of function literals, e.g. when - // declaring package level variables. - - isStdlibPath := func(path string) bool { - // Modules with no dot in the first path element are reserved for the standard library and tooling. - // This is the best we can currently do. - // Nobody tells us which import paths are part of the standard library. - // - // We check the entire path instead of just the first path element, because the standard library doesn't contain paths with any dots, anyway. - - return !strings.Contains(path, ".") - } - - handleDeprecation := func(depr *deprecated.IsDeprecated, node ast.Node, deprecatedObjName string, pkgPath string, tfn types.Object) { - // Note: gopls doesn't correctly run analyzers on - // dependencies, so we'll never be able to find deprecated - // objects in imported code. We've experimented with - // lifting the stdlib handling out of the general check, - // to at least work for deprecated objects in the stdlib, - // but we gave up on that, because we wouldn't have access - // to the deprecation message. - std, ok := knowledge.StdlibDeprecations[deprecatedObjName] - if !ok && isStdlibPath(pkgPath) { - // Deprecated object in the standard library, but we don't know the details of the deprecation. - // Don't flag it at all, to avoid flagging an object that was deprecated in 1.N when targeting 1.N-1. - // See https://staticcheck.io/issues/1108 for the background on this. - return - } - if ok { - // In the past, we made use of the AlternativeAvailableSince field. If a function was deprecated in Go - // 1.6 and an alternative had been available in Go 1.0, then we'd recommend using the alternative even - // if targeting Go 1.2. The idea was to suggest writing future-proof code by using already-existing - // alternatives. This had a major flaw, however: the user would need to use at least Go 1.6 for - // Staticcheck to know that the function had been deprecated. Thus, targeting Go 1.2 and using Go 1.2 - // would behave differently from targeting Go 1.2 and using Go 1.6. This is especially a problem if the - // user tries to ignore the warning. Depending on the Go version in use, the ignore directive may or may - // not match, causing a warning of its own. - // - // To avoid this issue, we no longer try to be smart. We now only compare the targeted version against - // the version that deprecated an object. - // - // Unfortunately, this issue also applies to AlternativeAvailableSince == DeprecatedNeverUse. Even though it - // is only applied to seriously flawed API, such as broken cryptography, users may wish to ignore those - // warnings. - // - // See also https://staticcheck.io/issues/1318. - if !code.IsGoVersion(pass, std.DeprecatedSince) { - return - } - } - - if tfn != nil { - if _, ok := deprs.Objects[tfn]; ok { - // functions that are deprecated may use deprecated - // symbols - return - } - } - - if ok { - switch std.AlternativeAvailableSince { - case knowledge.DeprecatedNeverUse: - report.Report(pass, node, - fmt.Sprintf("%s has been deprecated since Go 1.%d because it shouldn't be used: %s", - report.Render(pass, node), std.DeprecatedSince, depr.Msg)) - case std.DeprecatedSince, knowledge.DeprecatedUseNoLonger: - report.Report(pass, node, - fmt.Sprintf("%s has been deprecated since Go 1.%d: %s", - report.Render(pass, node), std.DeprecatedSince, depr.Msg)) - default: - report.Report(pass, node, - fmt.Sprintf("%s has been deprecated since Go 1.%d and an alternative has been available since Go 1.%d: %s", - report.Render(pass, node), std.DeprecatedSince, std.AlternativeAvailableSince, depr.Msg)) - } - } else { - report.Report(pass, node, fmt.Sprintf("%s is deprecated: %s", report.Render(pass, node), depr.Msg)) - } - } - - var tfn types.Object - stack := 0 - fn := func(node ast.Node, push bool) bool { - if !push { - stack-- - return false - } - stack++ - if stack == 1 { - tfn = nil - } - if fn, ok := node.(*ast.FuncDecl); ok { - tfn = pass.TypesInfo.ObjectOf(fn.Name) - } - - // FIXME(dh): this misses dot-imported objects - sel, ok := node.(*ast.SelectorExpr) - if !ok { - return true - } - - obj := pass.TypesInfo.ObjectOf(sel.Sel) - if obj_, ok := obj.(*types.Func); ok { - obj = obj_.Origin() - } - if obj.Pkg() == nil { - return true - } - - if obj.Pkg() == pass.Pkg { - // A package is allowed to use its own deprecated objects - return true - } - - // A package "foo" has two related packages "foo_test" and "foo.test", for external tests and the package main - // generated by 'go test' respectively. "foo_test" can import and use "foo", "foo.test" imports and uses "foo" - // and "foo_test". - - if strings.TrimSuffix(pass.Pkg.Path(), "_test") == obj.Pkg().Path() { - // foo_test (the external tests of foo) can use objects from foo. - return true - } - if strings.TrimSuffix(pass.Pkg.Path(), ".test") == obj.Pkg().Path() { - // foo.test (the main package of foo's tests) can use objects from foo. - return true - } - if strings.TrimSuffix(pass.Pkg.Path(), ".test") == strings.TrimSuffix(obj.Pkg().Path(), "_test") { - // foo.test (the main package of foo's tests) can use objects from foo's external tests. - return true - } - - if depr, ok := deprs.Objects[obj]; ok { - handleDeprecation(depr, sel, code.SelectorName(pass, sel), obj.Pkg().Path(), tfn) - } - return true - } - - fn2 := func(node ast.Node) { - spec := node.(*ast.ImportSpec) - var imp *types.Package - if spec.Name != nil { - imp = pass.TypesInfo.ObjectOf(spec.Name).(*types.PkgName).Imported() - } else { - imp = pass.TypesInfo.Implicits[spec].(*types.PkgName).Imported() - } - - p := spec.Path.Value - path := p[1 : len(p)-1] - if depr, ok := deprs.Packages[imp]; ok { - if path == "github.com/golang/protobuf/proto" { - gen, ok := code.Generator(pass, spec.Path.Pos()) - if ok && gen == generated.ProtocGenGo { - return - } - } - - if strings.TrimSuffix(pass.Pkg.Path(), "_test") == path { - // foo_test can import foo - return - } - if strings.TrimSuffix(pass.Pkg.Path(), ".test") == path { - // foo.test can import foo - return - } - if strings.TrimSuffix(pass.Pkg.Path(), ".test") == strings.TrimSuffix(path, "_test") { - // foo.test can import foo_test - return - } - - handleDeprecation(depr, spec.Path, path, path, nil) - } - } - pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Nodes(nil, fn) - code.Preorder(pass, fn2, (*ast.ImportSpec)(nil)) - return nil, nil -} - -func callChecker(rules map[string]CallCheck) func(pass *analysis.Pass) (interface{}, error) { - return func(pass *analysis.Pass) (interface{}, error) { - return checkCalls(pass, rules) - } -} - -func checkCalls(pass *analysis.Pass, rules map[string]CallCheck) (interface{}, error) { - cb := func(caller *ir.Function, site ir.CallInstruction, callee *ir.Function) { - obj, ok := callee.Object().(*types.Func) - if !ok { - return - } - - r, ok := rules[typeutil.FuncName(obj)] - if !ok { - return - } - var args []*Argument - irargs := site.Common().Args - if callee.Signature.Recv() != nil { - irargs = irargs[1:] - } - for _, arg := range irargs { - if iarg, ok := arg.(*ir.MakeInterface); ok { - arg = iarg.X - } - args = append(args, &Argument{Value: Value{arg}}) - } - call := &Call{ - Pass: pass, - Instr: site, - Args: args, - Parent: site.Parent(), - } - r(call) - - var astcall *ast.CallExpr - switch source := site.Source().(type) { - case *ast.CallExpr: - astcall = source - case *ast.DeferStmt: - astcall = source.Call - case *ast.GoStmt: - astcall = source.Call - case nil: - // TODO(dh): I am not sure this can actually happen. If it - // can't, we should remove this case, and also stop - // checking for astcall == nil in the code that follows. - default: - panic(fmt.Sprintf("unhandled case %T", source)) - } - - for idx, arg := range call.Args { - for _, e := range arg.invalids { - if astcall != nil { - if idx < len(astcall.Args) { - report.Report(pass, astcall.Args[idx], e) - } else { - // this is an instance of fn1(fn2()) where fn2 - // returns multiple values. Report the error - // at the next-best position that we have, the - // first argument. An example of a check that - // triggers this is checkEncodingBinaryRules. - report.Report(pass, astcall.Args[0], e) - } - } else { - report.Report(pass, site, e) - } - } - } - for _, e := range call.invalids { - report.Report(pass, call.Instr, e) - } - } - for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { - eachCall(fn, cb) - } - return nil, nil -} - -func shortCallName(call *ir.CallCommon) string { - if call.IsInvoke() { - return "" - } - switch v := call.Value.(type) { - case *ir.Function: - fn, ok := v.Object().(*types.Func) - if !ok { - return "" - } - return fn.Name() - case *ir.Builtin: - return v.Name() - } - return "" -} - -func CheckWriterBufferModified(pass *analysis.Pass) (interface{}, error) { - // TODO(dh): this might be a good candidate for taint analysis. - // Taint the argument as MUST_NOT_MODIFY, then propagate that - // through functions like bytes.Split - - for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { - sig := fn.Signature - if fn.Name() != "Write" || sig.Recv() == nil || sig.Params().Len() != 1 || sig.Results().Len() != 2 { - continue - } - tArg, ok := sig.Params().At(0).Type().(*types.Slice) - if !ok { - continue - } - if basic, ok := tArg.Elem().(*types.Basic); !ok || basic.Kind() != types.Byte { - continue - } - if basic, ok := sig.Results().At(0).Type().(*types.Basic); !ok || basic.Kind() != types.Int { - continue - } - if named, ok := sig.Results().At(1).Type().(*types.Named); !ok || !typeutil.IsType(named, "error") { - continue - } - - for _, block := range fn.Blocks { - for _, ins := range block.Instrs { - switch ins := ins.(type) { - case *ir.Store: - addr, ok := ins.Addr.(*ir.IndexAddr) - if !ok { - continue - } - if addr.X != fn.Params[1] { - continue - } - report.Report(pass, ins, "io.Writer.Write must not modify the provided buffer, not even temporarily") - case *ir.Call: - if !irutil.IsCallTo(ins.Common(), "append") { - continue - } - if ins.Common().Args[0] != fn.Params[1] { - continue - } - report.Report(pass, ins, "io.Writer.Write must not modify the provided buffer, not even temporarily") - } - } - } - } - return nil, nil -} - -func loopedRegexp(name string) CallCheck { - return func(call *Call) { - if extractConst(call.Args[0].Value.Value) == nil { - return - } - if !isInLoop(call.Instr.Block()) { - return - } - call.Invalid(fmt.Sprintf("calling %s in a loop has poor performance, consider using regexp.Compile", name)) - } -} - -func CheckEmptyBranch(pass *analysis.Pass) (interface{}, error) { - for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { - if fn.Source() == nil { - continue - } - if irutil.IsExample(fn) { - continue - } - cb := func(node ast.Node) bool { - ifstmt, ok := node.(*ast.IfStmt) - if !ok { - return true - } - if ifstmt.Else != nil { - b, ok := ifstmt.Else.(*ast.BlockStmt) - if !ok || len(b.List) != 0 { - return true - } - report.Report(pass, ifstmt.Else, "empty branch", report.FilterGenerated(), report.ShortRange()) - } - if len(ifstmt.Body.List) != 0 { - return true - } - report.Report(pass, ifstmt, "empty branch", report.FilterGenerated(), report.ShortRange()) - return true - } - if source := fn.Source(); source != nil { - ast.Inspect(source, cb) - } - } - return nil, nil -} - -func CheckMapBytesKey(pass *analysis.Pass) (interface{}, error) { - for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { - for _, b := range fn.Blocks { - insLoop: - for _, ins := range b.Instrs { - // find []byte -> string conversions - conv, ok := ins.(*ir.Convert) - if !ok || conv.Type() != types.Universe.Lookup("string").Type() { - continue - } - tset := typeutil.NewTypeSet(conv.X.Type()) - // If at least one of the types is []byte, then it's more efficient to inline the conversion - if !tset.Any(func(term *types.Term) bool { - s, ok := term.Type().Underlying().(*types.Slice) - return ok && s.Elem().Underlying() == types.Universe.Lookup("byte").Type() - }) { - continue - } - refs := conv.Referrers() - // need at least two (DebugRef) references: the - // conversion and the *ast.Ident - if refs == nil || len(*refs) < 2 { - continue - } - ident := false - // skip first reference, that's the conversion itself - for _, ref := range (*refs)[1:] { - switch ref := ref.(type) { - case *ir.DebugRef: - if _, ok := ref.Expr.(*ast.Ident); !ok { - // the string seems to be used somewhere - // unexpected; the default branch should - // catch this already, but be safe - continue insLoop - } else { - ident = true - } - case *ir.MapLookup: - default: - // the string is used somewhere else than a - // map lookup - continue insLoop - } - } - - // the result of the conversion wasn't assigned to an - // identifier - if !ident { - continue - } - report.Report(pass, conv, "m[string(key)] would be more efficient than k := string(key); m[k]") - } - } - } - return nil, nil -} - -func CheckRangeStringRunes(pass *analysis.Pass) (interface{}, error) { - return sharedcheck.CheckRangeStringRunes(pass) -} - -func CheckSelfAssignment(pass *analysis.Pass) (interface{}, error) { - pure := pass.ResultOf[purity.Analyzer].(purity.Result) - - fn := func(node ast.Node) { - assign := node.(*ast.AssignStmt) - if assign.Tok != token.ASSIGN || len(assign.Lhs) != len(assign.Rhs) { - return - } - for i, lhs := range assign.Lhs { - rhs := assign.Rhs[i] - if reflect.TypeOf(lhs) != reflect.TypeOf(rhs) { - continue - } - if code.MayHaveSideEffects(pass, lhs, pure) || code.MayHaveSideEffects(pass, rhs, pure) { - continue - } - - rlh := report.Render(pass, lhs) - rrh := report.Render(pass, rhs) - if rlh == rrh { - report.Report(pass, assign, fmt.Sprintf("self-assignment of %s to %s", rrh, rlh), report.FilterGenerated()) - } - } - } - code.Preorder(pass, fn, (*ast.AssignStmt)(nil)) - return nil, nil -} - -func buildTagsIdentical(s1, s2 []string) bool { - if len(s1) != len(s2) { - return false - } - s1s := make([]string, len(s1)) - copy(s1s, s1) - sort.Strings(s1s) - s2s := make([]string, len(s2)) - copy(s2s, s2) - sort.Strings(s2s) - for i, s := range s1s { - if s != s2s[i] { - return false - } - } - return true -} - -func CheckDuplicateBuildConstraints(pass *analysis.Pass) (interface{}, error) { - for _, f := range pass.Files { - constraints := buildTags(f) - for i, constraint1 := range constraints { - for j, constraint2 := range constraints { - if i >= j { - continue - } - if buildTagsIdentical(constraint1, constraint2) { - msg := fmt.Sprintf("identical build constraints %q and %q", - strings.Join(constraint1, " "), - strings.Join(constraint2, " ")) - report.Report(pass, f, msg, report.FilterGenerated(), report.ShortRange()) - } - } - } - } - return nil, nil -} - -func CheckSillyRegexp(pass *analysis.Pass) (interface{}, error) { - // We could use the rule checking engine for this, but the - // arguments aren't really invalid. - for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { - for _, b := range fn.Blocks { - for _, ins := range b.Instrs { - call, ok := ins.(*ir.Call) - if !ok { - continue - } - if !irutil.IsCallToAny(call.Common(), "regexp.MustCompile", "regexp.Compile", "regexp.Match", "regexp.MatchReader", "regexp.MatchString") { - continue - } - c, ok := call.Common().Args[0].(*ir.Const) - if !ok { - continue - } - s := constant.StringVal(c.Value) - re, err := syntax.Parse(s, 0) - if err != nil { - continue - } - if re.Op != syntax.OpLiteral && re.Op != syntax.OpEmptyMatch { - continue - } - report.Report(pass, call, "regular expression does not contain any meta characters") - } - } - } - return nil, nil -} - -func CheckMissingEnumTypesInDeclaration(pass *analysis.Pass) (interface{}, error) { - convertibleTo := func(V, T types.Type) bool { - if types.ConvertibleTo(V, T) { - return true - } - // Go <1.16 returns false for untyped string to string conversion - if V, ok := V.(*types.Basic); ok && V.Kind() == types.UntypedString { - if T, ok := T.Underlying().(*types.Basic); ok && T.Kind() == types.String { - return true - } - } - return false - } - fn := func(node ast.Node) { - decl := node.(*ast.GenDecl) - if !decl.Lparen.IsValid() { - return - } - if decl.Tok != token.CONST { - return - } - - groups := astutil.GroupSpecs(pass.Fset, decl.Specs) - groupLoop: - for _, group := range groups { - if len(group) < 2 { - continue - } - if group[0].(*ast.ValueSpec).Type == nil { - // first constant doesn't have a type - continue groupLoop - } - - firstType := pass.TypesInfo.TypeOf(group[0].(*ast.ValueSpec).Values[0]) - for i, spec := range group { - spec := spec.(*ast.ValueSpec) - if i > 0 && spec.Type != nil { - continue groupLoop - } - if len(spec.Names) != 1 || len(spec.Values) != 1 { - continue groupLoop - } - - if !convertibleTo(pass.TypesInfo.TypeOf(spec.Values[0]), firstType) { - continue groupLoop - } - - switch v := spec.Values[0].(type) { - case *ast.BasicLit: - case *ast.UnaryExpr: - if _, ok := v.X.(*ast.BasicLit); !ok { - continue groupLoop - } - default: - // if it's not a literal it might be typed, such as - // time.Microsecond = 1000 * Nanosecond - continue groupLoop - } - } - var edits []analysis.TextEdit - typ := group[0].(*ast.ValueSpec).Type - for _, spec := range group[1:] { - nspec := *spec.(*ast.ValueSpec) - nspec.Type = typ - // The position of `spec` node excludes comments (if any). - // However, on generating the source back from the node, the comments are included. Setting `Comment` to nil ensures deduplication of comments. - nspec.Comment = nil - edits = append(edits, edit.ReplaceWithNode(pass.Fset, spec, &nspec)) - } - report.Report(pass, group[0], "only the first constant in this group has an explicit type", report.Fixes(edit.Fix("add type to all constants in group", edits...))) - } - } - code.Preorder(pass, fn, (*ast.GenDecl)(nil)) - return nil, nil -} - -func CheckTimerResetReturnValue(pass *analysis.Pass) (interface{}, error) { - for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { - for _, block := range fn.Blocks { - for _, ins := range block.Instrs { - call, ok := ins.(*ir.Call) - if !ok { - continue - } - if !irutil.IsCallTo(call.Common(), "(*time.Timer).Reset") { - continue - } - refs := call.Referrers() - if refs == nil { - continue - } - for _, ref := range irutil.FilterDebug(*refs) { - ifstmt, ok := ref.(*ir.If) - if !ok { - continue - } - - found := false - for _, succ := range ifstmt.Block().Succs { - if len(succ.Preds) != 1 { - // Merge point, not a branch in the - // syntactical sense. - - // FIXME(dh): this is broken for if - // statements a la "if x || y" - continue - } - irutil.Walk(succ, func(b *ir.BasicBlock) bool { - if !succ.Dominates(b) { - // We've reached the end of the branch - return false - } - for _, ins := range b.Instrs { - // TODO(dh): we should check that - // we're receiving from the channel of - // a time.Timer to further reduce - // false positives. Not a key - // priority, considering the rarity of - // Reset and the tiny likeliness of a - // false positive - if ins, ok := ins.(*ir.Recv); ok && typeutil.IsType(ins.Chan.Type(), "<-chan time.Time") { - found = true - return false - } - } - return true - }) - } - - if found { - report.Report(pass, call, "it is not possible to use Reset's return value correctly, as there is a race condition between draining the channel and the new timer expiring") - } - } - } - } - } - return nil, nil -} - -var ( - checkToLowerToUpperComparisonQ = pattern.MustParse(` - (BinaryExpr - (CallExpr fun@(Symbol (Or "strings.ToLower" "strings.ToUpper")) [a]) - tok@(Or "==" "!=") - (CallExpr fun [b]))`) - checkToLowerToUpperComparisonR = pattern.MustParse(`(CallExpr (SelectorExpr (Ident "strings") (Ident "EqualFold")) [a b])`) -) - -func CheckToLowerToUpperComparison(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - m, ok := code.Match(pass, checkToLowerToUpperComparisonQ, node) - if !ok { - return - } - rn := pattern.NodeToAST(checkToLowerToUpperComparisonR.Root, m.State).(ast.Expr) - if m.State["tok"].(token.Token) == token.NEQ { - rn = &ast.UnaryExpr{ - Op: token.NOT, - X: rn, - } - } - - report.Report(pass, node, "should use strings.EqualFold instead", report.Fixes(edit.Fix("replace with strings.EqualFold", edit.ReplaceWithNode(pass.Fset, node, rn)))) - } - - code.Preorder(pass, fn, (*ast.BinaryExpr)(nil)) - return nil, nil -} - -func CheckUnreachableTypeCases(pass *analysis.Pass) (interface{}, error) { - // Check if T subsumes V in a type switch. T subsumes V if T is an interface and T's method set is a subset of V's method set. - subsumes := func(T, V types.Type) bool { - if typeparams.IsTypeParam(T) { - return false - } - tIface, ok := T.Underlying().(*types.Interface) - if !ok { - return false - } - - return types.Implements(V, tIface) - } - - subsumesAny := func(Ts, Vs []types.Type) (types.Type, types.Type, bool) { - for _, T := range Ts { - for _, V := range Vs { - if subsumes(T, V) { - return T, V, true - } - } - } - - return nil, nil, false - } - - fn := func(node ast.Node) { - tsStmt := node.(*ast.TypeSwitchStmt) - - type ccAndTypes struct { - cc *ast.CaseClause - types []types.Type - } - - // All asserted types in the order of case clauses. - ccs := make([]ccAndTypes, 0, len(tsStmt.Body.List)) - for _, stmt := range tsStmt.Body.List { - cc, _ := stmt.(*ast.CaseClause) - - // Exclude the 'default' case. - if len(cc.List) == 0 { - continue - } - - Ts := make([]types.Type, 0, len(cc.List)) - for _, expr := range cc.List { - // Exclude the 'nil' value from any 'case' statement (it is always reachable). - if typ := pass.TypesInfo.TypeOf(expr); typ != types.Typ[types.UntypedNil] { - Ts = append(Ts, typ) - } - } - - ccs = append(ccs, ccAndTypes{cc: cc, types: Ts}) - } - - if len(ccs) <= 1 { - // Zero or one case clauses, nothing to check. - return - } - - // Check if case clauses following cc have types that are subsumed by cc. - for i, cc := range ccs[:len(ccs)-1] { - for _, next := range ccs[i+1:] { - if T, V, yes := subsumesAny(cc.types, next.types); yes { - report.Report(pass, next.cc, fmt.Sprintf("unreachable case clause: %s will always match before %s", T.String(), V.String()), - report.ShortRange()) - } - } - } - } - - code.Preorder(pass, fn, (*ast.TypeSwitchStmt)(nil)) - return nil, nil -} - -var checkSingleArgAppendQ = pattern.MustParse(`(CallExpr (Builtin "append") [_])`) - -func CheckSingleArgAppend(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - _, ok := code.Match(pass, checkSingleArgAppendQ, node) - if !ok { - return - } - report.Report(pass, node, "x = append(y) is equivalent to x = y", report.FilterGenerated()) - } - code.Preorder(pass, fn, (*ast.CallExpr)(nil)) - return nil, nil -} - -func CheckStructTags(pass *analysis.Pass) (interface{}, error) { - importsGoFlags := false - - // we use the AST instead of (*types.Package).Imports to work - // around vendored packages in GOPATH mode. A vendored package's - // path will include the vendoring subtree as a prefix. - for _, f := range pass.Files { - for _, imp := range f.Imports { - v := imp.Path.Value - if v[1:len(v)-1] == "github.com/jessevdk/go-flags" { - importsGoFlags = true - break - } - } - } - - fn := func(node ast.Node) { - structNode := node.(*ast.StructType) - T := pass.TypesInfo.Types[structNode].Type.(*types.Struct) - rt := fakereflect.TypeAndCanAddr{ - Type: T, - } - for i, field := range structNode.Fields.List { - if field.Tag == nil { - continue - } - tags, err := parseStructTag(field.Tag.Value[1 : len(field.Tag.Value)-1]) - if err != nil { - report.Report(pass, field.Tag, fmt.Sprintf("unparseable struct tag: %s", err)) - continue - } - for k, v := range tags { - if len(v) > 1 { - isGoFlagsTag := importsGoFlags && - (k == "choice" || k == "optional-value" || k == "default") - if !isGoFlagsTag { - report.Report(pass, field.Tag, fmt.Sprintf("duplicate struct tag %q", k)) - } - } - - switch k { - case "json": - checkJSONTag(pass, field, v[0]) - case "xml": - if _, err := fakexml.StructFieldInfo(rt.Field(i)); err != nil { - report.Report(pass, field.Tag, fmt.Sprintf("invalid XML tag: %s", err)) - } - checkXMLTag(pass, field, v[0]) - } - } - } - } - code.Preorder(pass, fn, (*ast.StructType)(nil)) - return nil, nil -} - -func checkJSONTag(pass *analysis.Pass, field *ast.Field, tag string) { - if pass.Pkg.Path() == "encoding/json" || pass.Pkg.Path() == "encoding/json_test" { - // don't flag malformed JSON tags in the encoding/json - // package; it knows what it is doing, and it is testing - // itself. - return - } - //lint:ignore SA9003 TODO(dh): should we flag empty tags? - if len(tag) == 0 { - } - if i := strings.Index(tag, ",format:"); i >= 0 { - tag = tag[:i] - } - fields := strings.Split(tag, ",") - for _, r := range fields[0] { - if !unicode.IsLetter(r) && !unicode.IsDigit(r) && !strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", r) { - report.Report(pass, field.Tag, fmt.Sprintf("invalid JSON field name %q", fields[0])) - } - } - options := make(map[string]int) - for _, s := range fields[1:] { - switch s { - case "": - // allow stuff like "-," - case "string": - // only for string, floating point, integer and bool - options[s]++ - tset := typeutil.NewTypeSet(pass.TypesInfo.TypeOf(field.Type)) - if len(tset.Terms) == 0 { - // TODO(dh): improve message, call out the use of type parameters - report.Report(pass, field.Tag, "the JSON string option only applies to fields of type string, floating point, integer or bool, or pointers to those") - continue - } - for _, term := range tset.Terms { - T := typeutil.Dereference(term.Type().Underlying()) - for _, term2 := range typeutil.NewTypeSet(T).Terms { - basic, ok := term2.Type().Underlying().(*types.Basic) - if !ok || (basic.Info()&(types.IsBoolean|types.IsInteger|types.IsFloat|types.IsString)) == 0 { - // TODO(dh): improve message, show how we arrived at the type - report.Report(pass, field.Tag, "the JSON string option only applies to fields of type string, floating point, integer or bool, or pointers to those") - } - } - } - case "omitzero", "omitempty", "nocase", "inline", "unknown": - options[s]++ - default: - report.Report(pass, field.Tag, fmt.Sprintf("unknown JSON option %q", s)) - } - } - var duplicates []string - for option, n := range options { - if n > 1 { - duplicates = append(duplicates, option) - } - } - if len(duplicates) > 0 { - sort.Strings(duplicates) - for _, option := range duplicates { - report.Report(pass, field.Tag, fmt.Sprintf("duplicate JSON option %q", option)) - } - } -} - -func checkXMLTag(pass *analysis.Pass, field *ast.Field, tag string) { - //lint:ignore SA9003 TODO(dh): should we flag empty tags? - if len(tag) == 0 { - } - fields := strings.Split(tag, ",") - counts := map[string]int{} - for _, s := range fields[1:] { - switch s { - case "attr", "chardata", "cdata", "innerxml", "comment": - counts[s]++ - case "omitempty", "any": - counts[s]++ - case "": - default: - report.Report(pass, field.Tag, fmt.Sprintf("invalid XML tag: unknown option %q", s)) - } - } - for k, v := range counts { - if v > 1 { - report.Report(pass, field.Tag, fmt.Sprintf("invalid XML tag: duplicate option %q", k)) - } - } -} - -func CheckImpossibleTypeAssertion(pass *analysis.Pass) (interface{}, error) { - type entry struct { - l, r *types.Func - } - - msc := &pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg.Prog.MethodSets - for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { - for _, b := range fn.Blocks { - for _, instr := range b.Instrs { - assert, ok := instr.(*ir.TypeAssert) - if !ok { - continue - } - var wrong []entry - left := assert.X.Type() - right := assert.AssertedType - righti, ok := right.Underlying().(*types.Interface) - - if !ok { - // We only care about interface->interface - // assertions. The Go compiler already catches - // impossible interface->concrete assertions. - continue - } - - ms := msc.MethodSet(left) - for i := 0; i < righti.NumMethods(); i++ { - mr := righti.Method(i).Origin() - sel := ms.Lookup(mr.Pkg(), mr.Name()) - if sel == nil { - continue - } - ml := sel.Obj().(*types.Func).Origin() - if types.AssignableTo(ml.Type(), mr.Type()) { - continue - } - - wrong = append(wrong, entry{ml, mr}) - } - - if len(wrong) != 0 { - s := fmt.Sprintf("impossible type assertion; %s and %s contradict each other:", - types.TypeString(left, types.RelativeTo(pass.Pkg)), - types.TypeString(right, types.RelativeTo(pass.Pkg))) - for _, e := range wrong { - s += fmt.Sprintf("\n\twrong type for %s method", e.l.Name()) - s += fmt.Sprintf("\n\t\thave %s", e.l.Type()) - s += fmt.Sprintf("\n\t\twant %s", e.r.Type()) - } - report.Report(pass, assert, s) - } - } - } - } - return nil, nil -} - -func checkWithValueKey(call *Call) { - arg := call.Args[1] - T := arg.Value.Value.Type() - if T, ok := T.(*types.Basic); ok { - arg.Invalid( - fmt.Sprintf("should not use built-in type %s as key for value; define your own type to avoid collisions", T)) - } - if !types.Comparable(T) { - arg.Invalid(fmt.Sprintf("keys used with context.WithValue must be comparable, but type %s is not comparable", T)) - } -} - -func CheckMaybeNil(pass *analysis.Pass) (interface{}, error) { - // This is an extremely trivial check that doesn't try to reason - // about control flow. That is, phis and sigmas do not propagate - // any information. As such, we can flag this: - // - // _ = *x - // if x == nil { return } - // - // but we cannot flag this: - // - // if x == nil { println(x) } - // _ = *x - // - // but we can flag this, because the if's body doesn't use x: - // - // if x == nil { println("this is bad") } - // _ = *x - // - // nor many other variations of conditional uses of or assignments to x. - // - // However, even this trivial implementation finds plenty of - // real-world bugs, such as dereference before nil pointer check, - // or using t.Error instead of t.Fatal when encountering nil - // pointers. - // - // On the flip side, our naive implementation avoids false positives in branches, such as - // - // if x != nil { _ = *x } - // - // due to the same lack of propagating information through sigma - // nodes. x inside the branch will be independent of the x in the - // nil pointer check. - // - // - // We could implement a more powerful check, but then we'd be - // getting false positives instead of false negatives because - // we're incapable of deducing relationships between variables. - // For example, a function might return a pointer and an error, - // and the error being nil guarantees that the pointer is not nil. - // Depending on the surrounding code, the pointer may still end up - // being checked against nil in one place, and guarded by a check - // on the error in another, which would lead to us marking some - // loads as unsafe. - // - // Unfortunately, simply hard-coding the relationship between - // return values wouldn't eliminate all false positives, either. - // Many other more subtle relationships exist. An abridged example - // from real code: - // - // if a == nil && b == nil { return } - // c := fn(a) - // if c != "" { _ = *a } - // - // where `fn` is guaranteed to return a non-empty string if a - // isn't nil. - // - // We choose to err on the side of false negatives. - - isNilConst := func(v ir.Value) bool { - if typeutil.IsPointerLike(v.Type()) { - if k, ok := v.(*ir.Const); ok { - return k.IsNil() - } - } - return false - } - - for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { - maybeNil := map[ir.Value]ir.Instruction{} - for _, b := range fn.Blocks { - for _, instr := range b.Instrs { - // Originally we looked at all ir.BinOp, but that would lead to calls like 'assert(x != nil)' causing false positives. - // Restrict ourselves to actual if statements, as these are more likely to affect control flow in a way we can observe. - if instr, ok := instr.(*ir.If); ok { - if cond, ok := instr.Cond.(*ir.BinOp); ok { - var ptr ir.Value - if isNilConst(cond.X) { - ptr = cond.Y - } else if isNilConst(cond.Y) { - ptr = cond.X - } - maybeNil[ptr] = cond - } - } - } - } - - for _, b := range fn.Blocks { - for _, instr := range b.Instrs { - var ptr ir.Value - switch instr := instr.(type) { - case *ir.Load: - ptr = instr.X - case *ir.Store: - ptr = instr.Addr - case *ir.IndexAddr: - ptr = instr.X - if typeutil.All(ptr.Type(), func(term *types.Term) bool { - if _, ok := term.Type().Underlying().(*types.Slice); ok { - return true - } - return false - }) { - // indexing a nil slice does not cause a nil pointer panic - // - // Note: This also works around the bad lowering of range loops over slices - // (https://github.com/dominikh/go-tools/issues/1053) - continue - } - case *ir.FieldAddr: - ptr = instr.X - } - if ptr != nil { - switch ptr.(type) { - case *ir.Alloc, *ir.FieldAddr, *ir.IndexAddr: - // these cannot be nil - continue - } - if r, ok := maybeNil[ptr]; ok { - report.Report(pass, instr, "possible nil pointer dereference", - report.Related(r, "this check suggests that the pointer can be nil")) - } - } - } - } - } - - return nil, nil -} - -var checkAddressIsNilQ = pattern.MustParse( - `(BinaryExpr - (UnaryExpr "&" _) - (Or "==" "!=") - (Builtin "nil"))`) - -func CheckAddressIsNil(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - _, ok := code.Match(pass, checkAddressIsNilQ, node) - if !ok { - return - } - report.Report(pass, node, "the address of a variable cannot be nil") - } - code.Preorder(pass, fn, (*ast.BinaryExpr)(nil)) - return nil, nil -} - -var ( - checkFixedLengthTypeShiftQ = pattern.MustParse(` - (Or - (AssignStmt _ (Or ">>=" "<<=") _) - (BinaryExpr _ (Or ">>" "<<") _)) - `) -) - -func CheckStaticBitShift(pass *analysis.Pass) (interface{}, error) { - isDubiousShift := func(x, y ast.Expr) (int64, int64, bool) { - typ, ok := pass.TypesInfo.TypeOf(x).Underlying().(*types.Basic) - if !ok { - return 0, 0, false - } - switch typ.Kind() { - case types.Int8, types.Int16, types.Int32, types.Int64, - types.Uint8, types.Uint16, types.Uint32, types.Uint64: - // We're only interested in fixed–size types. - default: - return 0, 0, false - } - - const bitsInByte = 8 - typeBits := pass.TypesSizes.Sizeof(typ) * bitsInByte - - shiftLength, ok := code.ExprToInt(pass, y) - if !ok { - return 0, 0, false - } - - return typeBits, shiftLength, shiftLength >= typeBits - } - - fn := func(node ast.Node) { - if _, ok := code.Match(pass, checkFixedLengthTypeShiftQ, node); !ok { - return - } - - switch e := node.(type) { - case *ast.AssignStmt: - if size, shift, yes := isDubiousShift(e.Lhs[0], e.Rhs[0]); yes { - report.Report(pass, e, fmt.Sprintf("shifting %d-bit value by %d bits will always clear it", size, shift)) - } - case *ast.BinaryExpr: - if size, shift, yes := isDubiousShift(e.X, e.Y); yes { - report.Report(pass, e, fmt.Sprintf("shifting %d-bit value by %d bits will always clear it", size, shift)) - } - } - } - code.Preorder(pass, fn, (*ast.AssignStmt)(nil), (*ast.BinaryExpr)(nil)) - - return nil, nil -} - -func findSliceLenChecks(pass *analysis.Pass) { - // mark all function parameters that have to be of even length - for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { - for _, b := range fn.Blocks { - // all paths go through this block - if !b.Dominates(fn.Exit) { - continue - } - - // if foo % 2 != 0 - ifi, ok := b.Control().(*ir.If) - if !ok { - continue - } - cmp, ok := ifi.Cond.(*ir.BinOp) - if !ok { - continue - } - var needle uint64 - switch cmp.Op { - case token.NEQ: - // look for != 0 - needle = 0 - case token.EQL: - // look for == 1 - needle = 1 - default: - continue - } - - rem, ok1 := cmp.X.(*ir.BinOp) - k, ok2 := cmp.Y.(*ir.Const) - if ok1 != ok2 { - continue - } - if !ok1 { - rem, ok1 = cmp.Y.(*ir.BinOp) - k, ok2 = cmp.X.(*ir.Const) - } - if !ok1 || !ok2 || rem.Op != token.REM || k.Value.Kind() != constant.Int || k.Uint64() != needle { - continue - } - k, ok = rem.Y.(*ir.Const) - if !ok || k.Value.Kind() != constant.Int || k.Uint64() != 2 { - continue - } - - // if len(foo) % 2 != 0 - call, ok := rem.X.(*ir.Call) - if !ok || !irutil.IsCallTo(call.Common(), "len") { - continue - } - - // we're checking the length of a parameter that is a slice - // TODO(dh): support parameters that have flown through sigmas and phis - param, ok := call.Call.Args[0].(*ir.Parameter) - if !ok { - continue - } - if !typeutil.All(param.Type(), typeutil.IsSlice) { - continue - } - - // if len(foo) % 2 != 0 then panic - if _, ok := b.Succs[0].Control().(*ir.Panic); !ok { - continue - } - - pass.ExportObjectFact(param.Object(), new(evenElements)) - } - } -} - -func findIndirectSliceLenChecks(pass *analysis.Pass) { - seen := map[*ir.Function]struct{}{} - - var doFunction func(fn *ir.Function) - doFunction = func(fn *ir.Function) { - if _, ok := seen[fn]; ok { - return - } - seen[fn] = struct{}{} - - for _, b := range fn.Blocks { - // all paths go through this block - if !b.Dominates(fn.Exit) { - continue - } - - for _, instr := range b.Instrs { - call, ok := instr.(*ir.Call) - if !ok { - continue - } - callee := call.Call.StaticCallee() - if callee == nil { - continue - } - - if callee.Pkg == fn.Pkg || callee.Pkg == nil { - doFunction(callee) - } - - for argi, arg := range call.Call.Args { - if callee.Signature.Recv() != nil { - if argi == 0 { - continue - } - argi-- - } - - // TODO(dh): support parameters that have flown through length-preserving instructions - param, ok := arg.(*ir.Parameter) - if !ok { - continue - } - if !typeutil.All(param.Type(), typeutil.IsSlice) { - continue - } - - // We can't use callee.Params to look up the - // parameter, because Params is not populated for - // external functions. In our modular analysis. - // any function in any package that isn't the - // current package is considered "external", as it - // has been loaded from export data only. - sigParams := callee.Signature.Params() - - if !pass.ImportObjectFact(sigParams.At(argi), new(evenElements)) { - continue - } - pass.ExportObjectFact(param.Object(), new(evenElements)) - } - } - } - } - - for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { - doFunction(fn) - } -} - -func findSliceLength(v ir.Value) int { - // TODO(dh): VRP would help here - - v = irutil.Flatten(v) - val := func(v ir.Value) int { - if v, ok := v.(*ir.Const); ok { - return int(v.Int64()) - } - return -1 - } - switch v := v.(type) { - case *ir.Slice: - low := 0 - high := -1 - if v.Low != nil { - low = val(v.Low) - } - if v.High != nil { - high = val(v.High) - } else { - switch vv := v.X.(type) { - case *ir.Alloc: - high = int(typeutil.Dereference(vv.Type()).Underlying().(*types.Array).Len()) - case *ir.Slice: - high = findSliceLength(vv) - } - } - if low == -1 || high == -1 { - return -1 - } - return high - low - default: - return -1 - } -} - -type evenElements struct{} - -func (evenElements) AFact() {} - -func (evenElements) String() string { return "needs even elements" } - -func flagSliceLens(pass *analysis.Pass) { - var tag evenElements - - for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { - for _, b := range fn.Blocks { - for _, instr := range b.Instrs { - call, ok := instr.(ir.CallInstruction) - if !ok { - continue - } - callee := call.Common().StaticCallee() - if callee == nil { - continue - } - for argi, arg := range call.Common().Args { - if callee.Signature.Recv() != nil { - if argi == 0 { - continue - } - argi-- - } - - _, ok := arg.Type().Underlying().(*types.Slice) - if !ok { - continue - } - param := callee.Signature.Params().At(argi) - if !pass.ImportObjectFact(param, &tag) { - continue - } - - // TODO handle stubs - - // we know the argument has to have even length. - // now let's try to find its length - if n := findSliceLength(arg); n > -1 && n%2 != 0 { - src := call.Source().(*ast.CallExpr).Args[argi] - sig := call.Common().Signature() - var label string - if argi == sig.Params().Len()-1 && sig.Variadic() { - label = "variadic argument" - } else { - label = "argument" - } - // Note that param.Name() is guaranteed to not - // be empty, otherwise the function couldn't - // have enforced its length. - report.Report(pass, src, fmt.Sprintf("%s %q is expected to have even number of elements, but has %d elements", label, param.Name(), n)) - } - } - } - } - } -} - -func CheckEvenSliceLength(pass *analysis.Pass) (interface{}, error) { - findSliceLenChecks(pass) - findIndirectSliceLenChecks(pass) - flagSliceLens(pass) - - return nil, nil -} - -func CheckTypedNilInterface(pass *analysis.Pass) (interface{}, error) { - // The comparison 'fn() == nil' can never be true if fn() returns - // an interface value and only returns typed nils. This is usually - // a mistake in the function itself, but all we can say for - // certain is that the comparison is pointless. - // - // Flag results if no untyped nils are being returned, but either - // known typed nils, or typed unknown nilness are being returned. - - irpkg := pass.ResultOf[buildir.Analyzer].(*buildir.IR) - typedness := pass.ResultOf[typedness.Analysis].(*typedness.Result) - nilness := pass.ResultOf[nilness.Analysis].(*nilness.Result) - for _, fn := range irpkg.SrcFuncs { - for _, b := range fn.Blocks { - for _, instr := range b.Instrs { - binop, ok := instr.(*ir.BinOp) - if !ok || !(binop.Op == token.EQL || binop.Op == token.NEQ) { - continue - } - if _, ok := binop.X.Type().Underlying().(*types.Interface); !ok || typeparams.IsTypeParam(binop.X.Type()) { - // TODO support swapped X and Y - continue - } - - k, ok := binop.Y.(*ir.Const) - if !ok || !k.IsNil() { - // if binop.X is an interface, then binop.Y can - // only be a Const if its untyped. A typed nil - // constant would first be passed to - // MakeInterface. - continue - } - - var idx int - var obj *types.Func - switch x := irutil.Flatten(binop.X).(type) { - case *ir.Call: - callee := x.Call.StaticCallee() - if callee == nil { - continue - } - obj, _ = callee.Object().(*types.Func) - idx = 0 - case *ir.Extract: - call, ok := irutil.Flatten(x.Tuple).(*ir.Call) - if !ok { - continue - } - callee := call.Call.StaticCallee() - if callee == nil { - continue - } - obj, _ = callee.Object().(*types.Func) - idx = x.Index - case *ir.MakeInterface: - var qualifier string - switch binop.Op { - case token.EQL: - qualifier = "never" - case token.NEQ: - qualifier = "always" - default: - panic("unreachable") - } - - terms, err := typeparams.NormalTerms(x.X.Type()) - if len(terms) == 0 || err != nil { - // Type is a type parameter with no type terms (or we couldn't determine the terms). Such a type - // _can_ be nil when put in an interface value. - continue - } - - if report.HasRange(x.X) { - report.Report(pass, binop, fmt.Sprintf("this comparison is %s true", qualifier), - report.Related(x.X, "the lhs of the comparison gets its value from here and has a concrete type")) - } else { - // we can't generate related information for this, so make the diagnostic itself slightly more useful - report.Report(pass, binop, fmt.Sprintf("this comparison is %s true; the lhs of the comparison has been assigned a concretely typed value", qualifier)) - } - continue - } - if obj == nil { - continue - } - - isNil, onlyGlobal := nilness.MayReturnNil(obj, idx) - if typedness.MustReturnTyped(obj, idx) && isNil && !onlyGlobal && !code.IsInTest(pass, binop) { - // Don't flag these comparisons in tests. Tests - // may be explicitly enforcing the invariant that - // a value isn't nil. - - var qualifier string - switch binop.Op { - case token.EQL: - qualifier = "never" - case token.NEQ: - qualifier = "always" - default: - panic("unreachable") - } - report.Report(pass, binop, fmt.Sprintf("this comparison is %s true", qualifier), - // TODO support swapped X and Y - report.Related(binop.X, fmt.Sprintf("the lhs of the comparison is the %s return value of this function call", report.Ordinal(idx+1))), - report.Related(obj, fmt.Sprintf("%s never returns a nil interface value", typeutil.FuncName(obj)))) - } - } - } - } - - return nil, nil -} - -var builtinLessThanZeroQ = pattern.MustParse(` - (Or - (BinaryExpr - (IntegerLiteral "0") - ">" - (CallExpr builtin@(Builtin (Or "len" "cap")) _)) - (BinaryExpr - (CallExpr builtin@(Builtin (Or "len" "cap")) _) - "<" - (IntegerLiteral "0"))) -`) - -func CheckBuiltinZeroComparison(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - matcher, ok := code.Match(pass, builtinLessThanZeroQ, node) - if !ok { - return - } - - builtin := matcher.State["builtin"].(*ast.Ident) - report.Report(pass, node, fmt.Sprintf("builtin function %s does not return negative values", builtin.Name)) - } - code.Preorder(pass, fn, (*ast.BinaryExpr)(nil)) - - return nil, nil -} - -var integerDivisionQ = pattern.MustParse(`(BinaryExpr (IntegerLiteral _) "/" (IntegerLiteral _))`) - -func CheckIntegerDivisionEqualsZero(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - _, ok := code.Match(pass, integerDivisionQ, node) - if !ok { - return - } - - val := constant.ToInt(pass.TypesInfo.Types[node.(ast.Expr)].Value) - if v, ok := constant.Uint64Val(val); ok && v == 0 { - report.Report(pass, node, fmt.Sprintf("the integer division '%s' results in zero", report.Render(pass, node))) - } - - // TODO: we could offer a suggested fix here, but I am not - // sure what it should be. There are many options to choose - // from. - - // Note: we experimented with flagging divisions that truncate - // (e.g. 4 / 3), but it ran into false positives in Go's - // 'time' package, which does this, deliberately: - // - // unixToInternal int64 = (1969*365 + 1969/4 - 1969/100 + 1969/400) * secondsPerDay - // - // The check also found a real bug in other code, but I don't - // think we can outright ban this kind of division. - } - code.Preorder(pass, fn, (*ast.BinaryExpr)(nil)) - - return nil, nil -} - -func CheckIneffectiveFieldAssignments(pass *analysis.Pass) (interface{}, error) { - // The analysis only considers the receiver and its first level - // fields. It doesn't look at other parameters, nor at nested - // fields. - // - // The analysis does not detect all kinds of dead stores, only - // those of fields that are never read after the write. That is, - // we do not flag 'a.x = 1; a.x = 2; _ = a.x'. We might explore - // this again if we add support for SROA to go/ir and implement - // https://github.com/dominikh/go-tools/issues/191. - - irpkg := pass.ResultOf[buildir.Analyzer].(*buildir.IR) -fnLoop: - for _, fn := range irpkg.SrcFuncs { - if recv := fn.Signature.Recv(); recv == nil { - continue - } else if _, ok := recv.Type().Underlying().(*types.Struct); !ok { - continue - } - - recv := fn.Params[0] - refs := irutil.FilterDebug(*recv.Referrers()) - if len(refs) != 1 { - continue - } - store, ok := refs[0].(*ir.Store) - if !ok { - continue - } - alloc, ok := store.Addr.(*ir.Alloc) - if !ok || alloc.Heap { - continue - } - - reads := map[int][]ir.Instruction{} - writes := map[int][]ir.Instruction{} - for _, ref := range *alloc.Referrers() { - switch ref := ref.(type) { - case *ir.FieldAddr: - for _, refref := range *ref.Referrers() { - switch refref.(type) { - case *ir.Store: - writes[ref.Field] = append(writes[ref.Field], refref) - case *ir.Load: - reads[ref.Field] = append(reads[ref.Field], refref) - case *ir.DebugRef: - continue - default: - // this should be safe… if the field address - // escapes, then alloc.Heap will be true. - // there should be no instructions left that, - // given this FieldAddr, without escaping, can - // effect a load or store. - continue - } - } - case *ir.Store: - // we could treat this as a store to every field, but - // we don't want to decide the semantics of partial - // struct initializers. should `v = t{x: 1}` also mark - // v.y as being written to? - if ref != store { - continue fnLoop - } - case *ir.Load: - // a load of the entire struct loads every field - for i := 0; i < recv.Type().Underlying().(*types.Struct).NumFields(); i++ { - reads[i] = append(reads[i], ref) - } - case *ir.DebugRef: - continue - default: - continue fnLoop - } - } - - offset := func(instr ir.Instruction) int { - for i, other := range instr.Block().Instrs { - if instr == other { - return i - } - } - panic("couldn't find instruction in its block") - } - - for field, ws := range writes { - rs := reads[field] - wLoop: - for _, w := range ws { - for _, r := range rs { - if w.Block() == r.Block() { - if offset(r) > offset(w) { - // found a reachable read of our write - continue wLoop - } - } else if irutil.Reachable(w.Block(), r.Block()) { - // found a reachable read of our write - continue wLoop - } - } - fieldName := recv.Type().Underlying().(*types.Struct).Field(field).Name() - report.Report(pass, w, fmt.Sprintf("ineffective assignment to field %s.%s", recv.Type().(*types.Named).Obj().Name(), fieldName)) - } - } - } - return nil, nil -} - -var negativeZeroFloatQ = pattern.MustParse(` - (Or - (UnaryExpr - "-" - (BasicLit "FLOAT" "0.0")) - - (UnaryExpr - "-" - (CallExpr conv@(Object (Or "float32" "float64")) lit@(Or (BasicLit "INT" "0") (BasicLit "FLOAT" "0.0")))) - - (CallExpr - conv@(Object (Or "float32" "float64")) - (UnaryExpr "-" lit@(BasicLit "INT" "0"))))`) - -func CheckNegativeZeroFloat(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - m, ok := code.Match(pass, negativeZeroFloatQ, node) - if !ok { - return - } - - if conv, ok := m.State["conv"].(*types.TypeName); ok { - var replacement string - // TODO(dh): how does this handle type aliases? - if conv.Name() == "float32" { - replacement = `float32(math.Copysign(0, -1))` - } else { - replacement = `math.Copysign(0, -1)` - } - report.Report(pass, node, - fmt.Sprintf("in Go, the floating-point expression '%s' is the same as '%s(%s)', it does not produce a negative zero", - report.Render(pass, node), - conv.Name(), - report.Render(pass, m.State["lit"])), - report.Fixes(edit.Fix("use math.Copysign to create negative zero", edit.ReplaceWithString(node, replacement)))) - } else { - const replacement = `math.Copysign(0, -1)` - report.Report(pass, node, - "in Go, the floating-point literal '-0.0' is the same as '0.0', it does not produce a negative zero", - report.Fixes(edit.Fix("use math.Copysign to create negative zero", edit.ReplaceWithString(node, replacement)))) - } - } - code.Preorder(pass, fn, (*ast.UnaryExpr)(nil), (*ast.CallExpr)(nil)) - return nil, nil -} - -var ineffectiveURLQueryAddQ = pattern.MustParse(`(CallExpr (SelectorExpr (CallExpr (SelectorExpr recv (Ident "Query")) []) (Ident meth)) _)`) - -func CheckIneffectiveURLQueryModification(pass *analysis.Pass) (interface{}, error) { - // TODO(dh): We could make this check more complex and detect - // pointless modifications of net/url.Values in general, but that - // requires us to get the state machine correct, else we'll cause - // false positives. - - fn := func(node ast.Node) { - m, ok := code.Match(pass, ineffectiveURLQueryAddQ, node) - if !ok { - return - } - if !code.IsOfType(pass, m.State["recv"].(ast.Expr), "*net/url.URL") { - return - } - switch m.State["meth"].(string) { - case "Add", "Del", "Set": - default: - return - } - report.Report(pass, node, "(*net/url.URL).Query returns a copy, modifying it doesn't change the URL") - } - code.Preorder(pass, fn, (*ast.CallExpr)(nil)) - return nil, nil -} - -func CheckBadRemoveAll(pass *analysis.Pass) (interface{}, error) { - for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { - for _, b := range fn.Blocks { - for _, instr := range b.Instrs { - call, ok := instr.(ir.CallInstruction) - if !ok { - continue - } - if !irutil.IsCallTo(call.Common(), "os.RemoveAll") { - continue - } - - kind := "" - ex := "" - callName := "" - arg := irutil.Flatten(call.Common().Args[0]) - switch arg := arg.(type) { - case *ir.Call: - callName = irutil.CallName(&arg.Call) - if callName != "os.TempDir" { - continue - } - kind = "temporary" - ex = os.TempDir() - case *ir.Extract: - if arg.Index != 0 { - continue - } - first, ok := arg.Tuple.(*ir.Call) - if !ok { - continue - } - callName = irutil.CallName(&first.Call) - switch callName { - case "os.UserCacheDir": - kind = "cache" - ex, _ = os.UserCacheDir() - case "os.UserConfigDir": - kind = "config" - ex, _ = os.UserConfigDir() - case "os.UserHomeDir": - kind = "home" - ex, _ = os.UserHomeDir() - default: - continue - } - default: - continue - } - - if ex == "" { - report.Report(pass, call, fmt.Sprintf("this call to os.RemoveAll deletes the user's entire %s directory, not a subdirectory therein", kind), - report.Related(arg, fmt.Sprintf("this call to %s returns the user's %s directory", callName, kind))) - } else { - report.Report(pass, call, fmt.Sprintf("this call to os.RemoveAll deletes the user's entire %s directory, not a subdirectory therein", kind), - report.Related(arg, fmt.Sprintf("this call to %s returns the user's %s directory, for example %s", callName, kind, ex))) - } - } - } - } - return nil, nil -} - -var moduloOneQ = pattern.MustParse(`(BinaryExpr _ "%" (IntegerLiteral "1"))`) - -func CheckModuloOne(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - _, ok := code.Match(pass, moduloOneQ, node) - if !ok { - return - } - report.Report(pass, node, "x % 1 is always zero") - } - code.Preorder(pass, fn, (*ast.BinaryExpr)(nil)) - return nil, nil -} - -var typeAssertionShadowingElseQ = pattern.MustParse(`(IfStmt (AssignStmt [obj@(Ident _) ok@(Ident _)] ":=" assert@(TypeAssertExpr obj _)) ok _ elseBranch)`) - -func CheckTypeAssertionShadowingElse(pass *analysis.Pass) (interface{}, error) { - // TODO(dh): without the IR-based verification, this check is able - // to find more bugs, but also more prone to false positives. It - // would be a good candidate for the 'codereview' category of - // checks. - - irpkg := pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg - fn := func(node ast.Node) { - m, ok := code.Match(pass, typeAssertionShadowingElseQ, node) - if !ok { - return - } - shadow := pass.TypesInfo.ObjectOf(m.State["obj"].(*ast.Ident)) - shadowed := m.State["assert"].(*ast.TypeAssertExpr).X - - path, exact := astutil.PathEnclosingInterval(code.File(pass, shadow), shadow.Pos(), shadow.Pos()) - if !exact { - // TODO(dh): when can this happen? - return - } - irfn := ir.EnclosingFunction(irpkg, path) - if irfn == nil { - // For example for functions named "_", because we don't generate IR for them. - return - } - - shadoweeIR, isAddr := irfn.ValueForExpr(m.State["obj"].(*ast.Ident)) - if shadoweeIR == nil || isAddr { - // TODO(dh): is this possible? - return - } - - var branch ast.Node - switch br := m.State["elseBranch"].(type) { - case ast.Node: - branch = br - case []ast.Stmt: - branch = &ast.BlockStmt{List: br} - case nil: - return - default: - panic(fmt.Sprintf("unexpected type %T", br)) - } - - ast.Inspect(branch, func(node ast.Node) bool { - ident, ok := node.(*ast.Ident) - if !ok { - return true - } - if pass.TypesInfo.ObjectOf(ident) != shadow { - return true - } - - v, isAddr := irfn.ValueForExpr(ident) - if v == nil || isAddr { - return true - } - if irutil.Flatten(v) != shadoweeIR { - // Same types.Object, but different IR value. This - // either means that the variable has been - // assigned to since the type assertion, or that - // the variable has escaped to the heap. Either - // way, we shouldn't flag reads of it. - return true - } - - report.Report(pass, ident, - fmt.Sprintf("%s refers to the result of a failed type assertion and is a zero value, not the value that was being type-asserted", report.Render(pass, ident)), - report.Related(shadow, "this is the variable being read"), - report.Related(shadowed, "this is the variable being shadowed")) - return true - }) - } - code.Preorder(pass, fn, (*ast.IfStmt)(nil)) - return nil, nil -} - -var ineffectiveSortQ = pattern.MustParse(`(AssignStmt target@(Ident _) "=" (CallExpr typ@(Symbol (Or "sort.Float64Slice" "sort.IntSlice" "sort.StringSlice")) [target]))`) - -func CheckIneffectiveSort(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - m, ok := code.Match(pass, ineffectiveSortQ, node) - if !ok { - return - } - - _, ok = pass.TypesInfo.TypeOf(m.State["target"].(ast.Expr)).(*types.Slice) - if !ok { - // Avoid flagging 'x = sort.StringSlice(x)' where TypeOf(x) == sort.StringSlice - return - } - - var alternative string - typeName := types.TypeString(m.State["typ"].(*types.TypeName).Type(), nil) - switch typeName { - case "sort.Float64Slice": - alternative = "Float64s" - case "sort.IntSlice": - alternative = "Ints" - case "sort.StringSlice": - alternative = "Strings" - default: - panic(fmt.Sprintf("unreachable: %q", typeName)) - } - - r := &ast.CallExpr{ - Fun: &ast.SelectorExpr{ - X: &ast.Ident{Name: "sort"}, - Sel: &ast.Ident{Name: alternative}, - }, - Args: []ast.Expr{m.State["target"].(ast.Expr)}, - } - - report.Report(pass, node, - fmt.Sprintf("%s is a type, not a function, and %s doesn't sort your values; consider using sort.%s instead", - typeName, - report.Render(pass, node.(*ast.AssignStmt).Rhs[0]), - alternative), - report.Fixes(edit.Fix(fmt.Sprintf("replace with call to sort.%s", alternative), edit.ReplaceWithNode(pass.Fset, node, r)))) - } - code.Preorder(pass, fn, (*ast.AssignStmt)(nil)) - return nil, nil -} - -var ineffectiveRandIntQ = pattern.MustParse(` - (CallExpr - (Symbol - name@(Or - "math/rand.Int31n" - "math/rand.Int63n" - "math/rand.Intn" - "(*math/rand.Rand).Int31n" - "(*math/rand.Rand).Int63n" - "(*math/rand.Rand).Intn")) - [(IntegerLiteral "1")])`) - -func CheckIneffectiveRandInt(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - m, ok := code.Match(pass, ineffectiveRandIntQ, node) - if !ok { - return - } - - report.Report(pass, node, - fmt.Sprintf("%s(n) generates a random value 0 <= x < n; that is, the generated values don't include n; %s therefore always returns 0", - m.State["name"], report.Render(pass, node))) - } - - code.Preorder(pass, fn, (*ast.CallExpr)(nil)) - return nil, nil -} - -var allocationNilCheckQ = pattern.MustParse(`(IfStmt _ cond@(BinaryExpr lhs op@(Or "==" "!=") (Builtin "nil")) _ _)`) - -func CheckAllocationNilCheck(pass *analysis.Pass) (interface{}, error) { - irpkg := pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg - - var path []ast.Node - fn := func(node ast.Node, stack []ast.Node) { - m, ok := code.Match(pass, allocationNilCheckQ, node) - if !ok { - return - } - cond := m.State["cond"].(ast.Node) - if _, ok := code.Match(pass, checkAddressIsNilQ, cond); ok { - // Don't duplicate diagnostics reported by SA4022 - return - } - lhs := m.State["lhs"].(ast.Expr) - path = path[:0] - for i := len(stack) - 1; i >= 0; i-- { - path = append(path, stack[i]) - } - irfn := ir.EnclosingFunction(irpkg, path) - if irfn == nil { - // For example for functions named "_", because we don't generate IR for them. - return - } - v, isAddr := irfn.ValueForExpr(lhs) - if isAddr { - return - } - - seen := map[ir.Value]struct{}{} - var values []ir.Value - var neverNil func(v ir.Value, track bool) bool - neverNil = func(v ir.Value, track bool) bool { - if _, ok := seen[v]; ok { - return true - } - seen[v] = struct{}{} - switch v := v.(type) { - case *ir.MakeClosure, *ir.Function: - if track { - values = append(values, v) - } - return true - case *ir.MakeChan, *ir.MakeMap, *ir.MakeSlice, *ir.Alloc: - if track { - values = append(values, v) - } - return true - case *ir.Slice: - if track { - values = append(values, v) - } - return neverNil(v.X, false) - case *ir.FieldAddr: - if track { - values = append(values, v) - } - return neverNil(v.X, false) - case *ir.Sigma: - return neverNil(v.X, true) - case *ir.Phi: - for _, e := range v.Edges { - if !neverNil(e, true) { - return false - } - } - return true - default: - return false - } - } - - if !neverNil(v, true) { - return - } - - var qualifier string - if op := m.State["op"].(token.Token); op == token.EQL { - qualifier = "never" - } else { - qualifier = "always" - } - fallback := fmt.Sprintf("this nil check is %s true", qualifier) - - sort.Slice(values, func(i, j int) bool { return values[i].Pos() < values[j].Pos() }) - - if ident, ok := m.State["lhs"].(*ast.Ident); ok { - if _, ok := pass.TypesInfo.ObjectOf(ident).(*types.Var); ok { - var opts []report.Option - if v.Parent() == irfn { - if len(values) == 1 { - opts = append(opts, report.Related(values[0], fmt.Sprintf("this is the value of %s", ident.Name))) - } else { - for _, vv := range values { - opts = append(opts, report.Related(vv, fmt.Sprintf("this is one of the value of %s", ident.Name))) - } - } - } - - switch v.(type) { - case *ir.MakeClosure, *ir.Function: - report.Report(pass, cond, "the checked variable contains a function and is never nil; did you mean to call it?", opts...) - default: - report.Report(pass, cond, fallback, opts...) - } - } else { - if _, ok := v.(*ir.Function); ok { - report.Report(pass, cond, "functions are never nil; did you mean to call it?") - } else { - report.Report(pass, cond, fallback) - } - } - } else { - if _, ok := v.(*ir.Function); ok { - report.Report(pass, cond, "functions are never nil; did you mean to call it?") - } else { - report.Report(pass, cond, fallback) - } - } - } - code.PreorderStack(pass, fn, (*ast.IfStmt)(nil)) - return nil, nil -} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/rules.go b/tools/vendor/honnef.co/go/tools/staticcheck/rules.go deleted file mode 100644 index debdf5d230..0000000000 --- a/tools/vendor/honnef.co/go/tools/staticcheck/rules.go +++ /dev/null @@ -1,294 +0,0 @@ -package staticcheck - -import ( - "fmt" - "go/constant" - "go/types" - "net" - "net/url" - "regexp" - "sort" - "strconv" - "strings" - "time" - "unicode/utf8" - - "honnef.co/go/tools/analysis/code" - "honnef.co/go/tools/go/ir" - "honnef.co/go/tools/go/ir/irutil" - "honnef.co/go/tools/go/types/typeutil" - - "golang.org/x/tools/go/analysis" -) - -const ( - MsgInvalidHostPort = "invalid port or service name in host:port pair" - MsgInvalidUTF8 = "argument is not a valid UTF-8 encoded string" - MsgNonUniqueCutset = "cutset contains duplicate characters" -) - -type Call struct { - Pass *analysis.Pass - Instr ir.CallInstruction - Args []*Argument - - Parent *ir.Function - - invalids []string -} - -func (c *Call) Invalid(msg string) { - c.invalids = append(c.invalids, msg) -} - -type Argument struct { - Value Value - invalids []string -} - -type Value struct { - Value ir.Value -} - -func (arg *Argument) Invalid(msg string) { - arg.invalids = append(arg.invalids, msg) -} - -type CallCheck func(call *Call) - -func extractConstExpectKind(v ir.Value, kind constant.Kind) *ir.Const { - k := extractConst(v) - if k == nil || k.Value == nil || k.Value.Kind() != kind { - return nil - } - return k -} - -func extractConst(v ir.Value) *ir.Const { - v = irutil.Flatten(v) - switch v := v.(type) { - case *ir.Const: - return v - case *ir.MakeInterface: - return extractConst(v.X) - default: - return nil - } -} - -func ValidateRegexp(v Value) error { - if c := extractConstExpectKind(v.Value, constant.String); c != nil { - s := constant.StringVal(c.Value) - if _, err := regexp.Compile(s); err != nil { - return err - } - } - return nil -} - -func ValidateTimeLayout(v Value) error { - if c := extractConstExpectKind(v.Value, constant.String); c != nil { - s := constant.StringVal(c.Value) - s = strings.Replace(s, "_", " ", -1) - s = strings.Replace(s, "Z", "-", -1) - _, err := time.Parse(s, s) - if err != nil { - return err - } - } - return nil -} - -func ValidateURL(v Value) error { - if c := extractConstExpectKind(v.Value, constant.String); c != nil { - s := constant.StringVal(c.Value) - _, err := url.Parse(s) - if err != nil { - return fmt.Errorf("%q is not a valid URL: %s", s, err) - } - } - return nil -} - -func InvalidUTF8(v Value) bool { - if c := extractConstExpectKind(v.Value, constant.String); c != nil { - s := constant.StringVal(c.Value) - if !utf8.ValidString(s) { - return true - } - } - return false -} - -func UnbufferedChannel(v Value) bool { - // TODO(dh): this check of course misses many cases of unbuffered - // channels, such as any in phi or sigma nodes. We'll eventually - // replace this function. - val := v.Value - if ct, ok := val.(*ir.ChangeType); ok { - val = ct.X - } - mk, ok := val.(*ir.MakeChan) - if !ok { - return false - } - if k, ok := mk.Size.(*ir.Const); ok && k.Value.Kind() == constant.Int { - if v, ok := constant.Int64Val(k.Value); ok && v == 0 { - return true - } - } - return false -} - -func Pointer(v Value) bool { - switch v.Value.Type().Underlying().(type) { - case *types.Pointer, *types.Interface: - return true - } - return false -} - -func ConvertedFromInt(v Value) bool { - conv, ok := v.Value.(*ir.Convert) - if !ok { - return false - } - b, ok := conv.X.Type().Underlying().(*types.Basic) - if !ok { - return false - } - if (b.Info() & types.IsInteger) == 0 { - return false - } - return true -} - -func validEncodingBinaryType(pass *analysis.Pass, typ types.Type) bool { - typ = typ.Underlying() - switch typ := typ.(type) { - case *types.Basic: - switch typ.Kind() { - case types.Uint8, types.Uint16, types.Uint32, types.Uint64, - types.Int8, types.Int16, types.Int32, types.Int64, - types.Float32, types.Float64, types.Complex64, types.Complex128, types.Invalid: - return true - case types.Bool: - return code.IsGoVersion(pass, 8) - } - return false - case *types.Struct: - n := typ.NumFields() - for i := 0; i < n; i++ { - if !validEncodingBinaryType(pass, typ.Field(i).Type()) { - return false - } - } - return true - case *types.Array: - return validEncodingBinaryType(pass, typ.Elem()) - case *types.Interface: - // we can't determine if it's a valid type or not - return true - } - return false -} - -func CanBinaryMarshal(pass *analysis.Pass, v Value) bool { - typ := v.Value.Type().Underlying() - if ttyp, ok := typ.(*types.Pointer); ok { - typ = ttyp.Elem().Underlying() - } - if ttyp, ok := typ.(interface { - Elem() types.Type - }); ok { - if _, ok := ttyp.(*types.Pointer); !ok { - typ = ttyp.Elem() - } - } - - return validEncodingBinaryType(pass, typ) -} - -func RepeatZeroTimes(name string, arg int) CallCheck { - return func(call *Call) { - arg := call.Args[arg] - if k, ok := arg.Value.Value.(*ir.Const); ok && k.Value.Kind() == constant.Int { - if v, ok := constant.Int64Val(k.Value); ok && v == 0 { - arg.Invalid(fmt.Sprintf("calling %s with n == 0 will return no results, did you mean -1?", name)) - } - } - } -} - -func validateServiceName(s string) bool { - if len(s) < 1 || len(s) > 15 { - return false - } - if s[0] == '-' || s[len(s)-1] == '-' { - return false - } - if strings.Contains(s, "--") { - return false - } - hasLetter := false - for _, r := range s { - if (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') { - hasLetter = true - continue - } - if r >= '0' && r <= '9' { - continue - } - return false - } - return hasLetter -} - -func validatePort(s string) bool { - n, err := strconv.ParseInt(s, 10, 64) - if err != nil { - return validateServiceName(s) - } - return n >= 0 && n <= 65535 -} - -func ValidHostPort(v Value) bool { - if k := extractConstExpectKind(v.Value, constant.String); k != nil { - s := constant.StringVal(k.Value) - if s == "" { - return true - } - _, port, err := net.SplitHostPort(s) - if err != nil { - return false - } - // TODO(dh): check hostname - if !validatePort(port) { - return false - } - } - return true -} - -// ConvertedFrom reports whether value v was converted from type typ. -func ConvertedFrom(v Value, typ string) bool { - change, ok := v.Value.(*ir.ChangeType) - return ok && typeutil.IsType(change.X.Type(), typ) -} - -func UniqueStringCutset(v Value) bool { - if c := extractConstExpectKind(v.Value, constant.String); c != nil { - s := constant.StringVal(c.Value) - rs := runeSlice(s) - if len(rs) < 2 { - return true - } - sort.Sort(rs) - for i, r := range rs[1:] { - if rs[i] == r { - return false - } - } - } - return true -} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa1000/sa1000.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa1000/sa1000.go new file mode 100644 index 0000000000..3a1356ae32 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa1000/sa1000.go @@ -0,0 +1,46 @@ +package sa1000 + +import ( + "go/constant" + "regexp" + + "honnef.co/go/tools/analysis/callcheck" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/internal/passes/buildir" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA1000", + Requires: []*analysis.Analyzer{buildir.Analyzer}, + Run: callcheck.Analyzer(rules), + }, + Doc: &lint.RawDocumentation{ + Title: `Invalid regular expression`, + Since: "2017.1", + Severity: lint.SeverityError, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var rules = map[string]callcheck.Check{ + "regexp.MustCompile": check, + "regexp.Compile": check, + "regexp.Match": check, + "regexp.MatchReader": check, + "regexp.MatchString": check, +} + +func check(call *callcheck.Call) { + arg := call.Args[0] + if c := callcheck.ExtractConstExpectKind(arg.Value, constant.String); c != nil { + s := constant.StringVal(c.Value) + if _, err := regexp.Compile(s); err != nil { + arg.Invalid(err.Error()) + } + } +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa1001/sa1001.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa1001/sa1001.go new file mode 100644 index 0000000000..659a9a63b0 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa1001/sa1001.go @@ -0,0 +1,76 @@ +package sa1001 + +import ( + "go/ast" + htmltemplate "html/template" + "strings" + texttemplate "text/template" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/knowledge" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA1001", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Invalid template`, + Since: "2017.1", + Severity: lint.SeverityError, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + call := node.(*ast.CallExpr) + // OPT(dh): use integer for kind + var kind string + switch code.CallName(pass, call) { + case "(*text/template.Template).Parse": + kind = "text" + case "(*html/template.Template).Parse": + kind = "html" + default: + return + } + sel := call.Fun.(*ast.SelectorExpr) + if !code.IsCallToAny(pass, sel.X, "text/template.New", "html/template.New") { + // TODO(dh): this is a cheap workaround for templates with + // different delims. A better solution with less false + // negatives would use data flow analysis to see where the + // template comes from and where it has been + return + } + s, ok := code.ExprToString(pass, call.Args[knowledge.Arg("(*text/template.Template).Parse.text")]) + if !ok { + return + } + var err error + switch kind { + case "text": + _, err = texttemplate.New("").Parse(s) + case "html": + _, err = htmltemplate.New("").Parse(s) + } + if err != nil { + // TODO(dominikh): whitelist other parse errors, if any + if strings.Contains(err.Error(), "unexpected") || + strings.Contains(err.Error(), "bad character") { + report.Report(pass, call.Args[knowledge.Arg("(*text/template.Template).Parse.text")], err.Error()) + } + } + } + code.Preorder(pass, fn, (*ast.CallExpr)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa1002/sa1002.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa1002/sa1002.go new file mode 100644 index 0000000000..ddc2076752 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa1002/sa1002.go @@ -0,0 +1,45 @@ +package sa1002 + +import ( + "go/constant" + "strings" + "time" + + "honnef.co/go/tools/analysis/callcheck" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/internal/passes/buildir" + "honnef.co/go/tools/knowledge" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA1002", + Requires: []*analysis.Analyzer{buildir.Analyzer}, + Run: callcheck.Analyzer(rules), + }, + Doc: &lint.RawDocumentation{ + Title: `Invalid format in \'time.Parse\'`, + Since: "2017.1", + Severity: lint.SeverityError, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var rules = map[string]callcheck.Check{ + "time.Parse": func(call *callcheck.Call) { + arg := call.Args[knowledge.Arg("time.Parse.layout")] + if c := callcheck.ExtractConstExpectKind(arg.Value, constant.String); c != nil { + s := constant.StringVal(c.Value) + s = strings.Replace(s, "_", " ", -1) + s = strings.Replace(s, "Z", "-", -1) + _, err := time.Parse(s, s) + if err != nil { + arg.Invalid(err.Error()) + } + } + }, +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa1003/sa1003.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa1003/sa1003.go new file mode 100644 index 0000000000..4513ed244f --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa1003/sa1003.go @@ -0,0 +1,92 @@ +package sa1003 + +import ( + "fmt" + "go/types" + "go/version" + + "honnef.co/go/tools/analysis/callcheck" + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/internal/passes/buildir" + "honnef.co/go/tools/knowledge" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA1003", + Requires: []*analysis.Analyzer{buildir.Analyzer}, + Run: callcheck.Analyzer(checkEncodingBinaryRules), + }, + Doc: &lint.RawDocumentation{ + Title: `Unsupported argument to functions in \'encoding/binary\'`, + Text: `The \'encoding/binary\' package can only serialize types with known sizes. +This precludes the use of the \'int\' and \'uint\' types, as their sizes +differ on different architectures. Furthermore, it doesn't support +serializing maps, channels, strings, or functions. + +Before Go 1.8, \'bool\' wasn't supported, either.`, + Since: "2017.1", + Severity: lint.SeverityError, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var checkEncodingBinaryRules = map[string]callcheck.Check{ + "encoding/binary.Write": func(call *callcheck.Call) { + arg := call.Args[knowledge.Arg("encoding/binary.Write.data")] + if !CanBinaryMarshal(call.Pass, call.Parent, arg.Value) { + arg.Invalid(fmt.Sprintf("value of type %s cannot be used with binary.Write", arg.Value.Value.Type())) + } + }, +} + +func CanBinaryMarshal(pass *analysis.Pass, node code.Positioner, v callcheck.Value) bool { + typ := v.Value.Type().Underlying() + if ttyp, ok := typ.(*types.Pointer); ok { + typ = ttyp.Elem().Underlying() + } + if ttyp, ok := types.Unalias(typ).(interface { + Elem() types.Type + }); ok { + if _, ok := ttyp.(*types.Pointer); !ok { + typ = ttyp.Elem() + } + } + + return validEncodingBinaryType(pass, node, typ) +} + +func validEncodingBinaryType(pass *analysis.Pass, node code.Positioner, typ types.Type) bool { + typ = typ.Underlying() + switch typ := typ.(type) { + case *types.Basic: + switch typ.Kind() { + case types.Uint8, types.Uint16, types.Uint32, types.Uint64, + types.Int8, types.Int16, types.Int32, types.Int64, + types.Float32, types.Float64, types.Complex64, types.Complex128, types.Invalid: + return true + case types.Bool: + return version.Compare(code.StdlibVersion(pass, node), "go1.8") >= 0 + } + return false + case *types.Struct: + n := typ.NumFields() + for i := 0; i < n; i++ { + if !validEncodingBinaryType(pass, node, typ.Field(i).Type()) { + return false + } + } + return true + case *types.Array: + return validEncodingBinaryType(pass, node, typ.Elem()) + case *types.Interface: + // we can't determine if it's a valid type or not + return true + } + return false +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa1004/sa1004.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa1004/sa1004.go new file mode 100644 index 0000000000..c65de92716 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa1004/sa1004.go @@ -0,0 +1,78 @@ +package sa1004 + +import ( + "fmt" + "go/ast" + "go/constant" + "go/types" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/edit" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA1004", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Suspiciously small untyped constant in \'time.Sleep\'`, + Text: `The \'time\'.Sleep function takes a \'time.Duration\' as its only argument. +Durations are expressed in nanoseconds. Thus, calling \'time.Sleep(1)\' +will sleep for 1 nanosecond. This is a common source of bugs, as sleep +functions in other languages often accept seconds or milliseconds. + +The \'time\' package provides constants such as \'time.Second\' to express +large durations. These can be combined with arithmetic to express +arbitrary durations, for example \'5 * time.Second\' for 5 seconds. + +If you truly meant to sleep for a tiny amount of time, use +\'n * time.Nanosecond\' to signal to Staticcheck that you did mean to sleep +for some amount of nanoseconds.`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var ( + checkTimeSleepConstantPatternQ = pattern.MustParse(`(CallExpr (Symbol "time.Sleep") lit@(IntegerLiteral value))`) + checkTimeSleepConstantPatternRns = pattern.MustParse(`(BinaryExpr duration "*" (SelectorExpr (Ident "time") (Ident "Nanosecond")))`) + checkTimeSleepConstantPatternRs = pattern.MustParse(`(BinaryExpr duration "*" (SelectorExpr (Ident "time") (Ident "Second")))`) +) + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + m, ok := code.Match(pass, checkTimeSleepConstantPatternQ, node) + if !ok { + return + } + n, ok := constant.Int64Val(m.State["value"].(types.TypeAndValue).Value) + if !ok { + return + } + if n == 0 || n > 120 { + // time.Sleep(0) is a seldom used pattern in concurrency + // tests. >120 might be intentional. 120 was chosen + // because the user could've meant 2 minutes. + return + } + + lit := m.State["lit"].(ast.Node) + report.Report(pass, lit, + fmt.Sprintf("sleeping for %d nanoseconds is probably a bug; be explicit if it isn't", n), report.Fixes( + edit.Fix("explicitly use nanoseconds", edit.ReplaceWithPattern(pass.Fset, lit, checkTimeSleepConstantPatternRns, pattern.State{"duration": lit})), + edit.Fix("use seconds", edit.ReplaceWithPattern(pass.Fset, lit, checkTimeSleepConstantPatternRs, pattern.State{"duration": lit})))) + } + code.Preorder(pass, fn, (*ast.CallExpr)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa1005/sa1005.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa1005/sa1005.go new file mode 100644 index 0000000000..454dc01c6d --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa1005/sa1005.go @@ -0,0 +1,68 @@ +package sa1005 + +import ( + "go/ast" + "strings" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/knowledge" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA1005", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Invalid first argument to \'exec.Command\'`, + Text: `\'os/exec\' runs programs directly (using variants of the fork and exec +system calls on Unix systems). This shouldn't be confused with running +a command in a shell. The shell will allow for features such as input +redirection, pipes, and general scripting. The shell is also +responsible for splitting the user's input into a program name and its +arguments. For example, the equivalent to + + ls / /tmp + +would be + + exec.Command("ls", "/", "/tmp") + +If you want to run a command in a shell, consider using something like +the following – but be aware that not all systems, particularly +Windows, will have a \'/bin/sh\' program: + + exec.Command("/bin/sh", "-c", "ls | grep Awesome")`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + call := node.(*ast.CallExpr) + if !code.IsCallTo(pass, call, "os/exec.Command") { + return + } + val, ok := code.ExprToString(pass, call.Args[knowledge.Arg("os/exec.Command.name")]) + if !ok { + return + } + if !strings.Contains(val, " ") || strings.Contains(val, `\`) || strings.Contains(val, "/") { + return + } + report.Report(pass, call.Args[knowledge.Arg("os/exec.Command.name")], + "first argument to exec.Command looks like a shell command, but a program name or path are expected") + } + code.Preorder(pass, fn, (*ast.CallExpr)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa1006/sa1006.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa1006/sa1006.go new file mode 100644 index 0000000000..06b77436d9 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa1006/sa1006.go @@ -0,0 +1,107 @@ +package sa1006 + +import ( + "fmt" + "go/ast" + "go/types" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/edit" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/knowledge" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA1006", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `\'Printf\' with dynamic first argument and no further arguments`, + Text: `Using \'fmt.Printf\' with a dynamic first argument can lead to unexpected +output. The first argument is a format string, where certain character +combinations have special meaning. If, for example, a user were to +enter a string such as + + Interest rate: 5% + +and you printed it with + + fmt.Printf(s) + +it would lead to the following output: + + Interest rate: 5%!(NOVERB). + +Similarly, forming the first parameter via string concatenation with +user input should be avoided for the same reason. When printing user +input, either use a variant of \'fmt.Print\', or use the \'%s\' Printf verb +and pass the string as an argument.`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + call := node.(*ast.CallExpr) + name := code.CallName(pass, call) + var arg int + + switch name { + case "fmt.Errorf", "fmt.Printf", "fmt.Sprintf", + "log.Fatalf", "log.Panicf", "log.Printf", "(*log.Logger).Printf", + "(*testing.common).Logf", "(*testing.common).Errorf", + "(*testing.common).Fatalf", "(*testing.common).Skipf", + "(testing.TB).Logf", "(testing.TB).Errorf", + "(testing.TB).Fatalf", "(testing.TB).Skipf": + arg = knowledge.Arg("fmt.Printf.format") + case "fmt.Fprintf": + arg = knowledge.Arg("fmt.Fprintf.format") + default: + return + } + if len(call.Args) != arg+1 { + // This filters out calls of method expressions like (*log.Logger).Printf(nil, s) + return + } + switch call.Args[arg].(type) { + case *ast.CallExpr, *ast.Ident: + default: + return + } + + if _, ok := pass.TypesInfo.TypeOf(call.Args[arg]).(*types.Tuple); ok { + // the called function returns multiple values and got + // splatted into the call. for all we know, it is + // returning good arguments. + return + } + + var alt string + if name == "fmt.Errorf" { + // The alternative to fmt.Errorf isn't fmt.Error but errors.New + alt = "errors.New" + } else { + // This can be either a function call like log.Printf or a method call with an + // arbitrarily complex selector, such as foo.bar[0].Printf. In either case, + // all we have to do is remove the final 'f' from the existing call.Fun + // expression. + alt = report.Render(pass, call.Fun) + alt = alt[:len(alt)-1] + } + report.Report(pass, call, + "printf-style function with dynamic format string and no further arguments should use print-style function instead", + report.Fixes(edit.Fix(fmt.Sprintf("use %s instead of %s", alt, name), edit.ReplaceWithString(call.Fun, alt)))) + } + code.Preorder(pass, fn, (*ast.CallExpr)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa1007/sa1007.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa1007/sa1007.go new file mode 100644 index 0000000000..8aa6958ea1 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa1007/sa1007.go @@ -0,0 +1,43 @@ +package sa1007 + +import ( + "fmt" + "go/constant" + "net/url" + + "honnef.co/go/tools/analysis/callcheck" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/internal/passes/buildir" + "honnef.co/go/tools/knowledge" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA1007", + Requires: []*analysis.Analyzer{buildir.Analyzer}, + Run: callcheck.Analyzer(rules), + }, + Doc: &lint.RawDocumentation{ + Title: `Invalid URL in \'net/url.Parse\'`, + Since: "2017.1", + Severity: lint.SeverityError, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var rules = map[string]callcheck.Check{ + "net/url.Parse": func(call *callcheck.Call) { + arg := call.Args[knowledge.Arg("net/url.Parse.rawurl")] + if c := callcheck.ExtractConstExpectKind(arg.Value, constant.String); c != nil { + s := constant.StringVal(c.Value) + _, err := url.Parse(s) + if err != nil { + arg.Invalid(fmt.Sprintf("%q is not a valid URL: %s", s, err)) + } + } + }, +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa1008/sa1008.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa1008/sa1008.go new file mode 100644 index 0000000000..60e78d5be4 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa1008/sa1008.go @@ -0,0 +1,111 @@ +package sa1008 + +import ( + "fmt" + "go/ast" + "net/http" + "strconv" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/edit" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/inspector" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA1008", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Non-canonical key in \'http.Header\' map`, + Text: `Keys in \'http.Header\' maps are canonical, meaning they follow a specific +combination of uppercase and lowercase letters. Methods such as +\'http.Header.Add\' and \'http.Header.Del\' convert inputs into this canonical +form before manipulating the map. + +When manipulating \'http.Header\' maps directly, as opposed to using the +provided methods, care should be taken to stick to canonical form in +order to avoid inconsistencies. The following piece of code +demonstrates one such inconsistency: + + h := http.Header{} + h["etag"] = []string{"1234"} + h.Add("etag", "5678") + fmt.Println(h) + + // Output: + // map[Etag:[5678] etag:[1234]] + +The easiest way of obtaining the canonical form of a key is to use +\'http.CanonicalHeaderKey\'.`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node, push bool) bool { + if !push { + return false + } + if assign, ok := node.(*ast.AssignStmt); ok { + // TODO(dh): This risks missing some Header reads, for + // example in `h1["foo"] = h2["foo"]` – these edge + // cases are probably rare enough to ignore for now. + for _, expr := range assign.Lhs { + op, ok := expr.(*ast.IndexExpr) + if !ok { + continue + } + if code.IsOfTypeWithName(pass, op.X, "net/http.Header") { + return false + } + } + return true + } + op, ok := node.(*ast.IndexExpr) + if !ok { + return true + } + if !code.IsOfTypeWithName(pass, op.X, "net/http.Header") { + return true + } + s, ok := code.ExprToString(pass, op.Index) + if !ok { + return true + } + canonical := http.CanonicalHeaderKey(s) + if s == canonical { + return true + } + var fix analysis.SuggestedFix + switch op.Index.(type) { + case *ast.BasicLit: + fix = edit.Fix("canonicalize header key", edit.ReplaceWithString(op.Index, strconv.Quote(canonical))) + case *ast.Ident: + call := &ast.CallExpr{ + Fun: edit.Selector("http", "CanonicalHeaderKey"), + Args: []ast.Expr{op.Index}, + } + fix = edit.Fix("wrap in http.CanonicalHeaderKey", edit.ReplaceWithNode(pass.Fset, op.Index, call)) + } + msg := fmt.Sprintf("keys in http.Header are canonicalized, %q is not canonical; fix the constant or use http.CanonicalHeaderKey", s) + if fix.Message != "" { + report.Report(pass, op, msg, report.Fixes(fix)) + } else { + report.Report(pass, op, msg) + } + return true + } + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Nodes([]ast.Node{(*ast.AssignStmt)(nil), (*ast.IndexExpr)(nil)}, fn) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa1010/sa1010.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa1010/sa1010.go new file mode 100644 index 0000000000..2ad6b1fe40 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa1010/sa1010.go @@ -0,0 +1,53 @@ +package sa1010 + +import ( + "fmt" + "go/constant" + + "honnef.co/go/tools/analysis/callcheck" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/go/ir" + "honnef.co/go/tools/internal/passes/buildir" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA1010", + Requires: []*analysis.Analyzer{buildir.Analyzer}, + Run: callcheck.Analyzer(checkRegexpFindAllRules), + }, + Doc: &lint.RawDocumentation{ + Title: `\'(*regexp.Regexp).FindAll\' called with \'n == 0\', which will always return zero results`, + Text: `If \'n >= 0\', the function returns at most \'n\' matches/submatches. To +return all results, specify a negative number.`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, // MergeIfAny if we only flag literals, not named constants + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var checkRegexpFindAllRules = map[string]callcheck.Check{ + "(*regexp.Regexp).FindAll": RepeatZeroTimes("a FindAll method", 1), + "(*regexp.Regexp).FindAllIndex": RepeatZeroTimes("a FindAll method", 1), + "(*regexp.Regexp).FindAllString": RepeatZeroTimes("a FindAll method", 1), + "(*regexp.Regexp).FindAllStringIndex": RepeatZeroTimes("a FindAll method", 1), + "(*regexp.Regexp).FindAllStringSubmatch": RepeatZeroTimes("a FindAll method", 1), + "(*regexp.Regexp).FindAllStringSubmatchIndex": RepeatZeroTimes("a FindAll method", 1), + "(*regexp.Regexp).FindAllSubmatch": RepeatZeroTimes("a FindAll method", 1), + "(*regexp.Regexp).FindAllSubmatchIndex": RepeatZeroTimes("a FindAll method", 1), +} + +func RepeatZeroTimes(name string, arg int) callcheck.Check { + return func(call *callcheck.Call) { + arg := call.Args[arg] + if k, ok := arg.Value.Value.(*ir.Const); ok && k.Value.Kind() == constant.Int { + if v, ok := constant.Int64Val(k.Value); ok && v == 0 { + arg.Invalid(fmt.Sprintf("calling %s with n == 0 will return no results, did you mean -1?", name)) + } + } + } +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa1011/sa1011.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa1011/sa1011.go new file mode 100644 index 0000000000..5b86c6dede --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa1011/sa1011.go @@ -0,0 +1,47 @@ +package sa1011 + +import ( + "go/constant" + "unicode/utf8" + + "honnef.co/go/tools/analysis/callcheck" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/internal/passes/buildir" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA1011", + Requires: []*analysis.Analyzer{buildir.Analyzer}, + Run: callcheck.Analyzer(checkUTF8CutsetRules), + }, + Doc: &lint.RawDocumentation{ + Title: `Various methods in the \"strings\" package expect valid UTF-8, but invalid input is provided`, + Since: "2017.1", + Severity: lint.SeverityError, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var checkUTF8CutsetRules = map[string]callcheck.Check{ + "strings.IndexAny": check, + "strings.LastIndexAny": check, + "strings.ContainsAny": check, + "strings.Trim": check, + "strings.TrimLeft": check, + "strings.TrimRight": check, +} + +func check(call *callcheck.Call) { + arg := call.Args[1] + if c := callcheck.ExtractConstExpectKind(arg.Value, constant.String); c != nil { + s := constant.StringVal(c.Value) + if !utf8.ValidString(s) { + arg.Invalid("argument is not a valid UTF-8 encoded string") + } + } +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa1012/sa1012.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa1012/sa1012.go new file mode 100644 index 0000000000..1bf7712108 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa1012/sa1012.go @@ -0,0 +1,72 @@ +package sa1012 + +import ( + "go/ast" + "go/types" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/edit" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/types/typeutil" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA1012", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `A nil \'context.Context\' is being passed to a function, consider using \'context.TODO\' instead`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var checkNilContextQ = pattern.MustParse(`(CallExpr fun@(Symbol _) (Builtin "nil"):_)`) + +func run(pass *analysis.Pass) (interface{}, error) { + todo := &ast.CallExpr{ + Fun: edit.Selector("context", "TODO"), + } + bg := &ast.CallExpr{ + Fun: edit.Selector("context", "Background"), + } + fn := func(node ast.Node) { + m, ok := code.Match(pass, checkNilContextQ, node) + if !ok { + return + } + + call := node.(*ast.CallExpr) + fun, ok := m.State["fun"].(*types.Func) + if !ok { + // it might also be a builtin + return + } + sig := fun.Type().(*types.Signature) + if sig.Params().Len() == 0 { + // Our CallExpr might've matched a method expression, like + // (*T).Foo(nil) – here, nil isn't the first argument of + // the Foo method, but the method receiver. + return + } + if !typeutil.IsTypeWithName(sig.Params().At(0).Type(), "context.Context") { + return + } + report.Report(pass, call.Args[0], + "do not pass a nil Context, even if a function permits it; pass context.TODO if you are unsure about which Context to use", report.Fixes( + edit.Fix("use context.TODO", edit.ReplaceWithNode(pass.Fset, call.Args[0], todo)), + edit.Fix("use context.Background", edit.ReplaceWithNode(pass.Fset, call.Args[0], bg)))) + } + code.Preorder(pass, fn, (*ast.CallExpr)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa1013/sa1013.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa1013/sa1013.go new file mode 100644 index 0000000000..722baaa6f3 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa1013/sa1013.go @@ -0,0 +1,50 @@ +package sa1013 + +import ( + "go/ast" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/edit" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/knowledge" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA1013", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `\'io.Seeker.Seek\' is being called with the whence constant as the first argument, but it should be the second`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var ( + checkSeekerQ = pattern.MustParse(`(CallExpr fun@(SelectorExpr _ (Ident "Seek")) [arg1@(SelectorExpr _ (Symbol (Or "io.SeekStart" "io.SeekCurrent" "io.SeekEnd"))) arg2])`) + checkSeekerR = pattern.MustParse(`(CallExpr fun [arg2 arg1])`) +) + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + if m, edits, ok := code.MatchAndEdit(pass, checkSeekerQ, checkSeekerR, node); ok { + if !code.IsMethod(pass, m.State["fun"].(*ast.SelectorExpr), "Seek", knowledge.Signatures["(io.Seeker).Seek"]) { + return + } + report.Report(pass, node, "the first argument of io.Seeker is the offset, but an io.Seek* constant is being used instead", + report.Fixes(edit.Fix("swap arguments", edits...))) + } + } + code.Preorder(pass, fn, (*ast.CallExpr)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa1014/sa1014.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa1014/sa1014.go new file mode 100644 index 0000000000..90d676af26 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa1014/sa1014.go @@ -0,0 +1,52 @@ +package sa1014 + +import ( + "fmt" + "go/types" + + "honnef.co/go/tools/analysis/callcheck" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/internal/passes/buildir" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA1014", + Requires: []*analysis.Analyzer{buildir.Analyzer}, + Run: callcheck.Analyzer(checkUnmarshalPointerRules), + }, + Doc: &lint.RawDocumentation{ + Title: `Non-pointer value passed to \'Unmarshal\' or \'Decode\'`, + Since: "2017.1", + Severity: lint.SeverityError, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var checkUnmarshalPointerRules = map[string]callcheck.Check{ + "encoding/xml.Unmarshal": unmarshalPointer("xml.Unmarshal", 1), + "(*encoding/xml.Decoder).Decode": unmarshalPointer("Decode", 0), + "(*encoding/xml.Decoder).DecodeElement": unmarshalPointer("DecodeElement", 0), + "encoding/json.Unmarshal": unmarshalPointer("json.Unmarshal", 1), + "(*encoding/json.Decoder).Decode": unmarshalPointer("Decode", 0), +} + +func unmarshalPointer(name string, arg int) callcheck.Check { + return func(call *callcheck.Call) { + if !Pointer(call.Args[arg].Value) { + call.Args[arg].Invalid(fmt.Sprintf("%s expects to unmarshal into a pointer, but the provided value is not a pointer", name)) + } + } +} + +func Pointer(v callcheck.Value) bool { + switch v.Value.Type().Underlying().(type) { + case *types.Pointer, *types.Interface: + return true + } + return false +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa1015/sa1015.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa1015/sa1015.go new file mode 100644 index 0000000000..386c1a255e --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa1015/sa1015.go @@ -0,0 +1,69 @@ +package sa1015 + +import ( + "go/token" + "go/version" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/ir" + "honnef.co/go/tools/go/ir/irutil" + "honnef.co/go/tools/internal/passes/buildir" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA1015", + Run: run, + Requires: []*analysis.Analyzer{buildir.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Using \'time.Tick\' in a way that will leak. Consider using \'time.NewTicker\', and only use \'time.Tick\' in tests, commands and endless functions`, + + Text: `Before Go 1.23, \'time.Ticker\'s had to be closed to be able to be garbage +collected. Since \'time.Tick\' doesn't make it possible to close the underlying +ticker, using it repeatedly would leak memory. + +Go 1.23 fixes this by allowing tickers to be collected even if they weren't closed.`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { + if fn.Pos() == token.NoPos || version.Compare(code.StdlibVersion(pass, fn), "go1.23") >= 0 { + // Beginning with Go 1.23, the GC is able to collect unreferenced, unclosed + // tickers, which makes time.Tick safe(r) to use. + // + // When we don't have a valid position, we err on the side of false negatives. + // This shouldn't actually lead to any false negatives, as no functions + // without valid positions (such as the synthesized init function) should be + // able to use time.Tick. + continue + } + + if code.IsMainLike(pass) || code.IsInTest(pass, fn) { + continue + } + for _, block := range fn.Blocks { + for _, ins := range block.Instrs { + call, ok := ins.(*ir.Call) + if !ok || !irutil.IsCallTo(call.Common(), "time.Tick") { + continue + } + if !irutil.Terminates(call.Parent()) { + continue + } + report.Report(pass, call, "using time.Tick leaks the underlying ticker, consider using it only in endless functions, tests and the main package, and use time.NewTicker here") + } + } + } + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa1016/sa1016.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa1016/sa1016.go new file mode 100644 index 0000000000..fa9c620240 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa1016/sa1016.go @@ -0,0 +1,112 @@ +package sa1016 + +import ( + "fmt" + "go/ast" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/edit" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA1016", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Trapping a signal that cannot be trapped`, + Text: `Not all signals can be intercepted by a process. Specifically, on +UNIX-like systems, the \'syscall.SIGKILL\' and \'syscall.SIGSTOP\' signals are +never passed to the process, but instead handled directly by the +kernel. It is therefore pointless to try and handle these signals.`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + isSignal := func(pass *analysis.Pass, expr ast.Expr, name string) bool { + if expr, ok := expr.(*ast.SelectorExpr); ok { + return code.SelectorName(pass, expr) == name + } else { + return false + } + } + + fn := func(node ast.Node) { + call := node.(*ast.CallExpr) + if !code.IsCallToAny(pass, call, + "os/signal.Ignore", "os/signal.Notify", "os/signal.Reset") { + return + } + + hasSigterm := false + for _, arg := range call.Args { + if conv, ok := arg.(*ast.CallExpr); ok && isSignal(pass, conv.Fun, "os.Signal") { + arg = conv.Args[0] + } + + if isSignal(pass, arg, "syscall.SIGTERM") { + hasSigterm = true + break + } + + } + for i, arg := range call.Args { + if conv, ok := arg.(*ast.CallExpr); ok && isSignal(pass, conv.Fun, "os.Signal") { + arg = conv.Args[0] + } + + if isSignal(pass, arg, "os.Kill") || isSignal(pass, arg, "syscall.SIGKILL") { + var fixes []analysis.SuggestedFix + if !hasSigterm { + nargs := make([]ast.Expr, len(call.Args)) + for j, a := range call.Args { + if i == j { + nargs[j] = edit.Selector("syscall", "SIGTERM") + } else { + nargs[j] = a + } + } + ncall := *call + ncall.Args = nargs + fixes = append(fixes, edit.Fix(fmt.Sprintf("use syscall.SIGTERM instead of %s", report.Render(pass, arg)), edit.ReplaceWithNode(pass.Fset, call, &ncall))) + } + nargs := make([]ast.Expr, 0, len(call.Args)) + for j, a := range call.Args { + if i == j { + continue + } + nargs = append(nargs, a) + } + ncall := *call + ncall.Args = nargs + fixes = append(fixes, edit.Fix(fmt.Sprintf("remove %s from list of arguments", report.Render(pass, arg)), edit.ReplaceWithNode(pass.Fset, call, &ncall))) + report.Report(pass, arg, fmt.Sprintf("%s cannot be trapped (did you mean syscall.SIGTERM?)", report.Render(pass, arg)), report.Fixes(fixes...)) + } + if isSignal(pass, arg, "syscall.SIGSTOP") { + nargs := make([]ast.Expr, 0, len(call.Args)-1) + for j, a := range call.Args { + if i == j { + continue + } + nargs = append(nargs, a) + } + ncall := *call + ncall.Args = nargs + report.Report(pass, arg, "syscall.SIGSTOP cannot be trapped", report.Fixes(edit.Fix("remove syscall.SIGSTOP from list of arguments", edit.ReplaceWithNode(pass.Fset, call, &ncall)))) + } + } + } + code.Preorder(pass, fn, (*ast.CallExpr)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa1017/sa1017.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa1017/sa1017.go new file mode 100644 index 0000000000..f200d997a0 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa1017/sa1017.go @@ -0,0 +1,64 @@ +package sa1017 + +import ( + "go/constant" + + "honnef.co/go/tools/analysis/callcheck" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/go/ir" + "honnef.co/go/tools/internal/passes/buildir" + "honnef.co/go/tools/knowledge" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA1017", + Requires: []*analysis.Analyzer{buildir.Analyzer}, + Run: callcheck.Analyzer(rules), + }, + Doc: &lint.RawDocumentation{ + Title: `Channels used with \'os/signal.Notify\' should be buffered`, + Text: `The \'os/signal\' package uses non-blocking channel sends when delivering +signals. If the receiving end of the channel isn't ready and the +channel is either unbuffered or full, the signal will be dropped. To +avoid missing signals, the channel should be buffered and of the +appropriate size. For a channel used for notification of just one +signal value, a buffer of size 1 is sufficient.`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var rules = map[string]callcheck.Check{ + "os/signal.Notify": func(call *callcheck.Call) { + arg := call.Args[knowledge.Arg("os/signal.Notify.c")] + if isUnbufferedChannel(arg.Value) { + arg.Invalid("the channel used with signal.Notify should be buffered") + } + }, +} + +func isUnbufferedChannel(v callcheck.Value) bool { + // TODO(dh): this check of course misses many cases of unbuffered + // channels, such as any in phi or sigma nodes. We'll eventually + // replace this function. + val := v.Value + if ct, ok := val.(*ir.ChangeType); ok { + val = ct.X + } + mk, ok := val.(*ir.MakeChan) + if !ok { + return false + } + if k, ok := mk.Size.(*ir.Const); ok && k.Value.Kind() == constant.Int { + if v, ok := constant.Int64Val(k.Value); ok && v == 0 { + return true + } + } + return false +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa1018/sa1018.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa1018/sa1018.go new file mode 100644 index 0000000000..9981910eaa --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa1018/sa1018.go @@ -0,0 +1,47 @@ +package sa1018 + +import ( + "fmt" + "go/constant" + + "honnef.co/go/tools/analysis/callcheck" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/go/ir" + "honnef.co/go/tools/internal/passes/buildir" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA1018", + Requires: []*analysis.Analyzer{buildir.Analyzer}, + Run: callcheck.Analyzer(rules), + }, + Doc: &lint.RawDocumentation{ + Title: `\'strings.Replace\' called with \'n == 0\', which does nothing`, + Text: `With \'n == 0\', zero instances will be replaced. To replace all +instances, use a negative number, or use \'strings.ReplaceAll\'.`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, // MergeIfAny if we only flag literals, not named constants + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var rules = map[string]callcheck.Check{ + "strings.Replace": check("strings.Replace", 3), + "bytes.Replace": check("bytes.Replace", 3), +} + +func check(name string, arg int) callcheck.Check { + return func(call *callcheck.Call) { + arg := call.Args[arg] + if k, ok := arg.Value.Value.(*ir.Const); ok && k.Value.Kind() == constant.Int { + if v, ok := constant.Int64Val(k.Value); ok && v == 0 { + arg.Invalid(fmt.Sprintf("calling %s with n == 0 will return no results, did you mean -1?", name)) + } + } + } +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa1019/sa1019.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa1019/sa1019.go new file mode 100644 index 0000000000..50a400669e --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa1019/sa1019.go @@ -0,0 +1,212 @@ +package sa1019 + +import ( + "fmt" + "go/ast" + "go/types" + "go/version" + "strings" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/facts/deprecated" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/knowledge" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/inspector" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA1019", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer, deprecated.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Using a deprecated function, variable, constant or field`, + Since: "2017.1", + Severity: lint.SeverityDeprecated, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func formatGoVersion(s string) string { + return "Go " + strings.TrimPrefix(s, "go") +} + +func run(pass *analysis.Pass) (interface{}, error) { + deprs := pass.ResultOf[deprecated.Analyzer].(deprecated.Result) + + // Selectors can appear outside of function literals, e.g. when + // declaring package level variables. + + isStdlibPath := func(path string) bool { + // Modules with no dot in the first path element are reserved for the standard library and tooling. + // This is the best we can currently do. + // Nobody tells us which import paths are part of the standard library. + // + // We check the entire path instead of just the first path element, because the standard library doesn't contain paths with any dots, anyway. + + return !strings.Contains(path, ".") + } + + handleDeprecation := func(depr *deprecated.IsDeprecated, node ast.Node, deprecatedObjName string, pkgPath string, tfn types.Object) { + std, ok := knowledge.StdlibDeprecations[deprecatedObjName] + if !ok && isStdlibPath(pkgPath) { + // Deprecated object in the standard library, but we don't know the details of the deprecation. + // Don't flag it at all, to avoid flagging an object that was deprecated in 1.N when targeting 1.N-1. + // See https://staticcheck.dev/issues/1108 for the background on this. + return + } + if ok { + // In the past, we made use of the AlternativeAvailableSince field. If a function was deprecated in Go + // 1.6 and an alternative had been available in Go 1.0, then we'd recommend using the alternative even + // if targeting Go 1.2. The idea was to suggest writing future-proof code by using already-existing + // alternatives. This had a major flaw, however: the user would need to use at least Go 1.6 for + // Staticcheck to know that the function had been deprecated. Thus, targeting Go 1.2 and using Go 1.2 + // would behave differently from targeting Go 1.2 and using Go 1.6. This is especially a problem if the + // user tries to ignore the warning. Depending on the Go version in use, the ignore directive may or may + // not match, causing a warning of its own. + // + // To avoid this issue, we no longer try to be smart. We now only compare the targeted version against + // the version that deprecated an object. + // + // Unfortunately, this issue also applies to AlternativeAvailableSince == DeprecatedNeverUse. Even though it + // is only applied to seriously flawed API, such as broken cryptography, users may wish to ignore those + // warnings. + // + // See also https://staticcheck.dev/issues/1318. + if version.Compare(code.StdlibVersion(pass, node), std.DeprecatedSince) == -1 { + return + } + } + + if tfn != nil { + if _, ok := deprs.Objects[tfn]; ok { + // functions that are deprecated may use deprecated + // symbols + return + } + } + + if ok { + switch std.AlternativeAvailableSince { + case knowledge.DeprecatedNeverUse: + report.Report(pass, node, + fmt.Sprintf("%s has been deprecated since %s because it shouldn't be used: %s", + report.Render(pass, node), formatGoVersion(std.DeprecatedSince), depr.Msg)) + case std.DeprecatedSince, knowledge.DeprecatedUseNoLonger: + report.Report(pass, node, + fmt.Sprintf("%s has been deprecated since %s: %s", + report.Render(pass, node), formatGoVersion(std.DeprecatedSince), depr.Msg)) + default: + report.Report(pass, node, + fmt.Sprintf("%s has been deprecated since %s and an alternative has been available since %s: %s", + report.Render(pass, node), formatGoVersion(std.DeprecatedSince), formatGoVersion(std.AlternativeAvailableSince), depr.Msg)) + } + } else { + report.Report(pass, node, fmt.Sprintf("%s is deprecated: %s", report.Render(pass, node), depr.Msg)) + } + } + + var tfn types.Object + stack := 0 + fn := func(node ast.Node, push bool) bool { + if !push { + stack-- + return false + } + stack++ + if stack == 1 { + tfn = nil + } + if fn, ok := node.(*ast.FuncDecl); ok { + tfn = pass.TypesInfo.ObjectOf(fn.Name) + } + + // FIXME(dh): this misses dot-imported objects + sel, ok := node.(*ast.SelectorExpr) + if !ok { + return true + } + + obj := pass.TypesInfo.ObjectOf(sel.Sel) + if obj_, ok := obj.(*types.Func); ok { + obj = obj_.Origin() + } + if obj.Pkg() == nil { + return true + } + + if obj.Pkg() == pass.Pkg { + // A package is allowed to use its own deprecated objects + return true + } + + // A package "foo" has two related packages "foo_test" and "foo.test", for external tests and the package main + // generated by 'go test' respectively. "foo_test" can import and use "foo", "foo.test" imports and uses "foo" + // and "foo_test". + + if strings.TrimSuffix(pass.Pkg.Path(), "_test") == obj.Pkg().Path() { + // foo_test (the external tests of foo) can use objects from foo. + return true + } + if strings.TrimSuffix(pass.Pkg.Path(), ".test") == obj.Pkg().Path() { + // foo.test (the main package of foo's tests) can use objects from foo. + return true + } + if strings.TrimSuffix(pass.Pkg.Path(), ".test") == strings.TrimSuffix(obj.Pkg().Path(), "_test") { + // foo.test (the main package of foo's tests) can use objects from foo's external tests. + return true + } + + if depr, ok := deprs.Objects[obj]; ok { + handleDeprecation(depr, sel, code.SelectorName(pass, sel), obj.Pkg().Path(), tfn) + } + return true + } + + fn2 := func(node ast.Node) { + spec := node.(*ast.ImportSpec) + var imp *types.Package + if spec.Name != nil { + imp = pass.TypesInfo.ObjectOf(spec.Name).(*types.PkgName).Imported() + } else { + imp = pass.TypesInfo.Implicits[spec].(*types.PkgName).Imported() + } + + p := spec.Path.Value + path := p[1 : len(p)-1] + if depr, ok := deprs.Packages[imp]; ok { + if path == "github.com/golang/protobuf/proto" { + gen, ok := code.Generator(pass, spec.Path.Pos()) + if ok && gen == generated.ProtocGenGo { + return + } + } + + if strings.TrimSuffix(pass.Pkg.Path(), "_test") == path { + // foo_test can import foo + return + } + if strings.TrimSuffix(pass.Pkg.Path(), ".test") == path { + // foo.test can import foo + return + } + if strings.TrimSuffix(pass.Pkg.Path(), ".test") == strings.TrimSuffix(path, "_test") { + // foo.test can import foo_test + return + } + + handleDeprecation(depr, spec.Path, path, path, nil) + } + } + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Nodes(nil, fn) + code.Preorder(pass, fn2, (*ast.ImportSpec)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa1020/sa1020.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa1020/sa1020.go new file mode 100644 index 0000000000..aa1aeae65b --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa1020/sa1020.go @@ -0,0 +1,94 @@ +package sa1020 + +import ( + "go/constant" + "net" + "strconv" + "strings" + + "honnef.co/go/tools/analysis/callcheck" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/internal/passes/buildir" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA1020", + Requires: []*analysis.Analyzer{buildir.Analyzer}, + Run: callcheck.Analyzer(checkListenAddressRules), + }, + Doc: &lint.RawDocumentation{ + Title: `Using an invalid host:port pair with a \'net.Listen\'-related function`, + Since: "2017.1", + Severity: lint.SeverityError, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var checkListenAddressRules = map[string]callcheck.Check{ + "net/http.ListenAndServe": checkValidHostPort(0), + "net/http.ListenAndServeTLS": checkValidHostPort(0), +} + +func checkValidHostPort(arg int) callcheck.Check { + return func(call *callcheck.Call) { + if !ValidHostPort(call.Args[arg].Value) { + const MsgInvalidHostPort = "invalid port or service name in host:port pair" + call.Args[arg].Invalid(MsgInvalidHostPort) + } + } +} + +func ValidHostPort(v callcheck.Value) bool { + if k := callcheck.ExtractConstExpectKind(v, constant.String); k != nil { + s := constant.StringVal(k.Value) + if s == "" { + return true + } + _, port, err := net.SplitHostPort(s) + if err != nil { + return false + } + // TODO(dh): check hostname + if !validatePort(port) { + return false + } + } + return true +} + +func validateServiceName(s string) bool { + if len(s) < 1 || len(s) > 15 { + return false + } + if s[0] == '-' || s[len(s)-1] == '-' { + return false + } + if strings.Contains(s, "--") { + return false + } + hasLetter := false + for _, r := range s { + if (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') { + hasLetter = true + continue + } + if r >= '0' && r <= '9' { + continue + } + return false + } + return hasLetter +} + +func validatePort(s string) bool { + n, err := strconv.ParseInt(s, 10, 64) + if err != nil { + return validateServiceName(s) + } + return n >= 0 && n <= 65535 +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa1021/sa1021.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa1021/sa1021.go new file mode 100644 index 0000000000..95fc960215 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa1021/sa1021.go @@ -0,0 +1,49 @@ +package sa1021 + +import ( + "go/types" + + "honnef.co/go/tools/analysis/callcheck" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/go/ir" + "honnef.co/go/tools/internal/passes/buildir" + "honnef.co/go/tools/knowledge" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA1021", + Requires: []*analysis.Analyzer{buildir.Analyzer}, + Run: callcheck.Analyzer(rules), + }, + Doc: &lint.RawDocumentation{ + Title: `Using \'bytes.Equal\' to compare two \'net.IP\'`, + Text: `A \'net.IP\' stores an IPv4 or IPv6 address as a slice of bytes. The +length of the slice for an IPv4 address, however, can be either 4 or +16 bytes long, using different ways of representing IPv4 addresses. In +order to correctly compare two \'net.IP\'s, the \'net.IP.Equal\' method should +be used, as it takes both representations into account.`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var rules = map[string]callcheck.Check{ + "bytes.Equal": func(call *callcheck.Call) { + if isConvertedFrom(call.Args[knowledge.Arg("bytes.Equal.a")].Value, "net.IP") && + isConvertedFrom(call.Args[knowledge.Arg("bytes.Equal.b")].Value, "net.IP") { + call.Invalid("use net.IP.Equal to compare net.IPs, not bytes.Equal") + } + }, +} + +// ConvertedFrom reports whether value v was converted from type typ. +func isConvertedFrom(v callcheck.Value, typ string) bool { + change, ok := v.Value.(*ir.ChangeType) + return ok && types.TypeString(types.Unalias(change.X.Type()), nil) == typ +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa1023/sa1023.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa1023/sa1023.go new file mode 100644 index 0000000000..1ebfe7e935 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa1023/sa1023.go @@ -0,0 +1,72 @@ +package sa1023 + +import ( + "go/types" + + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/ir" + "honnef.co/go/tools/go/ir/irutil" + "honnef.co/go/tools/internal/passes/buildir" + "honnef.co/go/tools/knowledge" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA1023", + Run: run, + Requires: []*analysis.Analyzer{buildir.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Modifying the buffer in an \'io.Writer\' implementation`, + Text: `\'Write\' must not modify the slice data, even temporarily.`, + Since: "2017.1", + Severity: lint.SeverityError, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + // TODO(dh): this might be a good candidate for taint analysis. + // Taint the argument as MUST_NOT_MODIFY, then propagate that + // through functions like bytes.Split + + for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { + sig := fn.Signature + if fn.Name() != "Write" || sig.Recv() == nil { + continue + } + if !types.Identical(sig, knowledge.Signatures["(io.Writer).Write"]) { + continue + } + + for _, block := range fn.Blocks { + for _, ins := range block.Instrs { + switch ins := ins.(type) { + case *ir.Store: + addr, ok := ins.Addr.(*ir.IndexAddr) + if !ok { + continue + } + if addr.X != fn.Params[1] { + continue + } + report.Report(pass, ins, "io.Writer.Write must not modify the provided buffer, not even temporarily") + case *ir.Call: + if !irutil.IsCallTo(ins.Common(), "append") { + continue + } + if ins.Common().Args[0] != fn.Params[1] { + continue + } + report.Report(pass, ins, "io.Writer.Write must not modify the provided buffer, not even temporarily") + } + } + } + } + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa1024/sa1024.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa1024/sa1024.go new file mode 100644 index 0000000000..2ff4896841 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa1024/sa1024.go @@ -0,0 +1,75 @@ +package sa1024 + +import ( + "go/constant" + "sort" + + "honnef.co/go/tools/analysis/callcheck" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/internal/passes/buildir" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA1024", + Requires: []*analysis.Analyzer{buildir.Analyzer}, + Run: callcheck.Analyzer(rules), + }, + Doc: &lint.RawDocumentation{ + Title: `A string cutset contains duplicate characters`, + Text: `The \'strings.TrimLeft\' and \'strings.TrimRight\' functions take cutsets, not +prefixes. A cutset is treated as a set of characters to remove from a +string. For example, + + strings.TrimLeft("42133word", "1234") + +will result in the string \'"word"\' – any characters that are 1, 2, 3 or +4 are cut from the left of the string. + +In order to remove one string from another, use \'strings.TrimPrefix\' instead.`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var rules = map[string]callcheck.Check{ + "strings.Trim": check, + "strings.TrimLeft": check, + "strings.TrimRight": check, +} + +func check(call *callcheck.Call) { + arg := call.Args[1] + if !isUniqueStringCutset(arg.Value) { + const MsgNonUniqueCutset = "cutset contains duplicate characters" + arg.Invalid(MsgNonUniqueCutset) + } +} + +func isUniqueStringCutset(v callcheck.Value) bool { + if c := callcheck.ExtractConstExpectKind(v, constant.String); c != nil { + s := constant.StringVal(c.Value) + rs := runeSlice(s) + if len(rs) < 2 { + return true + } + sort.Sort(rs) + for i, r := range rs[1:] { + if rs[i] == r { + return false + } + } + } + return true +} + +type runeSlice []rune + +func (rs runeSlice) Len() int { return len(rs) } +func (rs runeSlice) Less(i int, j int) bool { return rs[i] < rs[j] } +func (rs runeSlice) Swap(i int, j int) { rs[i], rs[j] = rs[j], rs[i] } diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa1025/sa1025.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa1025/sa1025.go new file mode 100644 index 0000000000..9445c7d6e4 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa1025/sa1025.go @@ -0,0 +1,92 @@ +package sa1025 + +import ( + "go/types" + + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/ir" + "honnef.co/go/tools/go/ir/irutil" + "honnef.co/go/tools/internal/passes/buildir" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA1025", + Run: run, + Requires: []*analysis.Analyzer{buildir.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `It is not possible to use \'(*time.Timer).Reset\''s return value correctly`, + Since: "2019.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { + for _, block := range fn.Blocks { + for _, ins := range block.Instrs { + call, ok := ins.(*ir.Call) + if !ok { + continue + } + if !irutil.IsCallTo(call.Common(), "(*time.Timer).Reset") { + continue + } + refs := call.Referrers() + if refs == nil { + continue + } + for _, ref := range irutil.FilterDebug(*refs) { + ifstmt, ok := ref.(*ir.If) + if !ok { + continue + } + + found := false + for _, succ := range ifstmt.Block().Succs { + if len(succ.Preds) != 1 { + // Merge point, not a branch in the + // syntactical sense. + + // FIXME(dh): this is broken for if + // statements a la "if x || y" + continue + } + irutil.Walk(succ, func(b *ir.BasicBlock) bool { + if !succ.Dominates(b) { + // We've reached the end of the branch + return false + } + for _, ins := range b.Instrs { + // TODO(dh): we should check that we're receiving from the + // channel of a time.Timer to further reduce false + // positives. Not a key priority, considering the rarity + // of Reset and the tiny likeliness of a false positive + // + // We intentionally don't handle aliases here, because + // we're only interested in time.Timer.C. + if ins, ok := ins.(*ir.Recv); ok && types.TypeString(ins.Chan.Type(), nil) == "<-chan time.Time" { + found = true + return false + } + } + return true + }) + } + + if found { + report.Report(pass, call, "it is not possible to use Reset's return value correctly, as there is a race condition between draining the channel and the new timer expiring") + } + } + } + } + } + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa1026/sa1026.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa1026/sa1026.go new file mode 100644 index 0000000000..51ea62e5de --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa1026/sa1026.go @@ -0,0 +1,77 @@ +package sa1026 + +import ( + "fmt" + "go/types" + + "honnef.co/go/tools/analysis/callcheck" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/internal/passes/buildir" + "honnef.co/go/tools/staticcheck/fakejson" + "honnef.co/go/tools/staticcheck/fakexml" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA1026", + Requires: []*analysis.Analyzer{buildir.Analyzer}, + Run: callcheck.Analyzer(rules), + }, + Doc: &lint.RawDocumentation{ + Title: `Cannot marshal channels or functions`, + Since: "2019.2", + Severity: lint.SeverityError, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var rules = map[string]callcheck.Check{ + "encoding/json.Marshal": checkJSON, + "encoding/xml.Marshal": checkXML, + "(*encoding/json.Encoder).Encode": checkJSON, + "(*encoding/xml.Encoder).Encode": checkXML, +} + +func checkJSON(call *callcheck.Call) { + arg := call.Args[0] + T := arg.Value.Value.Type() + if err := fakejson.Marshal(T); err != nil { + typ := types.TypeString(err.Type, types.RelativeTo(arg.Value.Value.Parent().Pkg.Pkg)) + if err.Path == "x" { + arg.Invalid(fmt.Sprintf("trying to marshal unsupported type %s", typ)) + } else { + arg.Invalid(fmt.Sprintf("trying to marshal unsupported type %s, via %s", typ, err.Path)) + } + } +} + +func checkXML(call *callcheck.Call) { + arg := call.Args[0] + T := arg.Value.Value.Type() + if err := fakexml.Marshal(T); err != nil { + switch err := err.(type) { + case *fakexml.UnsupportedTypeError: + typ := types.TypeString(err.Type, types.RelativeTo(arg.Value.Value.Parent().Pkg.Pkg)) + if err.Path == "x" { + arg.Invalid(fmt.Sprintf("trying to marshal unsupported type %s", typ)) + } else { + arg.Invalid(fmt.Sprintf("trying to marshal unsupported type %s, via %s", typ, err.Path)) + } + case *fakexml.CyclicTypeError: + typ := types.TypeString(err.Type, types.RelativeTo(arg.Value.Value.Parent().Pkg.Pkg)) + if err.Path == "x" { + arg.Invalid(fmt.Sprintf("trying to marshal cyclic type %s", typ)) + } else { + arg.Invalid(fmt.Sprintf("trying to marshal cyclic type %s, via %s", typ, err.Path)) + } + case *fakexml.TagPathError: + // Vet does a better job at reporting this error, because it can flag the actual struct tags, not just the call to Marshal + default: + // These errors get reported by SA5008 instead, which can flag the actual fields, independently of calls to xml.Marshal + } + } +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa1027/sa1027.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa1027/sa1027.go new file mode 100644 index 0000000000..95633c4791 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa1027/sa1027.go @@ -0,0 +1,76 @@ +package sa1027 + +import ( + "fmt" + "go/types" + + "honnef.co/go/tools/analysis/callcheck" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/go/ir" + "honnef.co/go/tools/go/ir/irutil" + "honnef.co/go/tools/internal/passes/buildir" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA1027", + Requires: []*analysis.Analyzer{buildir.Analyzer}, + Run: callcheck.Analyzer(checkAtomicAlignment), + }, + Doc: &lint.RawDocumentation{ + Title: `Atomic access to 64-bit variable must be 64-bit aligned`, + Text: `On ARM, x86-32, and 32-bit MIPS, it is the caller's responsibility to +arrange for 64-bit alignment of 64-bit words accessed atomically. The +first word in a variable or in an allocated struct, array, or slice +can be relied upon to be 64-bit aligned. + +You can use the structlayout tool to inspect the alignment of fields +in a struct.`, + Since: "2019.2", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var checkAtomicAlignment = map[string]callcheck.Check{ + "sync/atomic.AddInt64": checkAtomicAlignmentImpl, + "sync/atomic.AddUint64": checkAtomicAlignmentImpl, + "sync/atomic.CompareAndSwapInt64": checkAtomicAlignmentImpl, + "sync/atomic.CompareAndSwapUint64": checkAtomicAlignmentImpl, + "sync/atomic.LoadInt64": checkAtomicAlignmentImpl, + "sync/atomic.LoadUint64": checkAtomicAlignmentImpl, + "sync/atomic.StoreInt64": checkAtomicAlignmentImpl, + "sync/atomic.StoreUint64": checkAtomicAlignmentImpl, + "sync/atomic.SwapInt64": checkAtomicAlignmentImpl, + "sync/atomic.SwapUint64": checkAtomicAlignmentImpl, +} + +func checkAtomicAlignmentImpl(call *callcheck.Call) { + sizes := call.Pass.TypesSizes + if sizes.Sizeof(types.Typ[types.Uintptr]) != 4 { + // Not running on a 32-bit platform + return + } + v, ok := irutil.Flatten(call.Args[0].Value.Value).(*ir.FieldAddr) + if !ok { + // TODO(dh): also check indexing into arrays and slices + return + } + T := v.X.Type().Underlying().(*types.Pointer).Elem().Underlying().(*types.Struct) + fields := make([]*types.Var, 0, T.NumFields()) + for i := 0; i < T.NumFields() && i <= v.Field; i++ { + fields = append(fields, T.Field(i)) + } + + off := sizes.Offsetsof(fields)[v.Field] + if off%8 != 0 { + msg := fmt.Sprintf("address of non 64-bit aligned field %s passed to %s", + T.Field(v.Field).Name(), + irutil.CallName(call.Instr.Common())) + call.Invalid(msg) + } +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa1028/sa1028.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa1028/sa1028.go new file mode 100644 index 0000000000..5ac1216d90 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa1028/sa1028.go @@ -0,0 +1,57 @@ +package sa1028 + +import ( + "fmt" + "go/types" + + "honnef.co/go/tools/analysis/callcheck" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/go/ir" + "honnef.co/go/tools/internal/passes/buildir" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA1028", + Requires: []*analysis.Analyzer{buildir.Analyzer}, + Run: callcheck.Analyzer(rules), + }, + Doc: &lint.RawDocumentation{ + Title: `\'sort.Slice\' can only be used on slices`, + Text: `The first argument of \'sort.Slice\' must be a slice.`, + Since: "2020.1", + Severity: lint.SeverityError, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var rules = map[string]callcheck.Check{ + "sort.Slice": check, + "sort.SliceIsSorted": check, + "sort.SliceStable": check, +} + +func check(call *callcheck.Call) { + c := call.Instr.Common().StaticCallee() + arg := call.Args[0] + + T := arg.Value.Value.Type().Underlying() + switch T.(type) { + case *types.Interface: + // we don't know. + // TODO(dh): if the value is a phi node we can look at its edges + if k, ok := arg.Value.Value.(*ir.Const); ok && k.Value == nil { + // literal nil, e.g. sort.Sort(nil, ...) + arg.Invalid(fmt.Sprintf("cannot call %s on nil literal", c)) + } + case *types.Slice: + // this is fine + default: + // this is not fine + arg.Invalid(fmt.Sprintf("%s must only be called on slices, was called on %s", c, T)) + } +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa1029/sa1029.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa1029/sa1029.go new file mode 100644 index 0000000000..c981baa2a6 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa1029/sa1029.go @@ -0,0 +1,61 @@ +package sa1029 + +import ( + "fmt" + "go/types" + + "honnef.co/go/tools/analysis/callcheck" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/internal/passes/buildir" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA1029", + Requires: []*analysis.Analyzer{buildir.Analyzer}, + Run: callcheck.Analyzer(checkWithValueKeyRules), + }, + Doc: &lint.RawDocumentation{ + Title: `Inappropriate key in call to \'context.WithValue\'`, + Text: `The provided key must be comparable and should not be +of type \'string\' or any other built-in type to avoid collisions between +packages using context. Users of \'WithValue\' should define their own +types for keys. + +To avoid allocating when assigning to an \'interface{}\', +context keys often have concrete type \'struct{}\'. Alternatively, +exported context key variables' static type should be a pointer or +interface.`, + Since: "2020.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var checkWithValueKeyRules = map[string]callcheck.Check{ + "context.WithValue": checkWithValueKey, +} + +func checkWithValueKey(call *callcheck.Call) { + arg := call.Args[1] + T := arg.Value.Value.Type() + if typ, ok := types.Unalias(T).(*types.Basic); ok { + if _, ok := T.(*types.Alias); ok { + arg.Invalid( + fmt.Sprintf("should not use built-in type %s (via alias %s) as key for value; define your own type to avoid collisions", typ, types.TypeString(T, types.RelativeTo(call.Pass.Pkg)))) + } else { + arg.Invalid( + fmt.Sprintf("should not use built-in type %s as key for value; define your own type to avoid collisions", typ)) + } + } + // TODO(dh): we should probably flag all anonymous structs, as they all risk collisions + if s, ok := T.(*types.Struct); ok && s.NumFields() == 0 { + arg.Invalid("should not use empty anonymous struct as key for value; define your own type to avoid collisions") + } else if !types.Comparable(T) { + arg.Invalid(fmt.Sprintf("keys used with context.WithValue must be comparable, but type %s is not comparable", T)) + } +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa1030/sa1030.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa1030/sa1030.go new file mode 100644 index 0000000000..1a274ce1ad --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa1030/sa1030.go @@ -0,0 +1,134 @@ +package sa1030 + +import ( + "fmt" + "go/constant" + + "honnef.co/go/tools/analysis/callcheck" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/internal/passes/buildir" + "honnef.co/go/tools/knowledge" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA1030", + Requires: []*analysis.Analyzer{buildir.Analyzer}, + Run: callcheck.Analyzer(rules), + }, + Doc: &lint.RawDocumentation{ + Title: `Invalid argument in call to a \'strconv\' function`, + Text: `This check validates the format, number base and bit size arguments of +the various parsing and formatting functions in \'strconv\'.`, + Since: "2021.1", + Severity: lint.SeverityError, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var rules = map[string]callcheck.Check{ + "strconv.ParseComplex": func(call *callcheck.Call) { + validateComplexBitSize(call.Args[knowledge.Arg("strconv.ParseComplex.bitSize")]) + }, + "strconv.ParseFloat": func(call *callcheck.Call) { + validateFloatBitSize(call.Args[knowledge.Arg("strconv.ParseFloat.bitSize")]) + }, + "strconv.ParseInt": func(call *callcheck.Call) { + validateContinuousBitSize(call.Args[knowledge.Arg("strconv.ParseInt.bitSize")], 0, 64) + validateIntBaseAllowZero(call.Args[knowledge.Arg("strconv.ParseInt.base")]) + }, + "strconv.ParseUint": func(call *callcheck.Call) { + validateContinuousBitSize(call.Args[knowledge.Arg("strconv.ParseUint.bitSize")], 0, 64) + validateIntBaseAllowZero(call.Args[knowledge.Arg("strconv.ParseUint.base")]) + }, + + "strconv.FormatComplex": func(call *callcheck.Call) { + validateComplexFormat(call.Args[knowledge.Arg("strconv.FormatComplex.fmt")]) + validateComplexBitSize(call.Args[knowledge.Arg("strconv.FormatComplex.bitSize")]) + }, + "strconv.FormatFloat": func(call *callcheck.Call) { + validateFloatFormat(call.Args[knowledge.Arg("strconv.FormatFloat.fmt")]) + validateFloatBitSize(call.Args[knowledge.Arg("strconv.FormatFloat.bitSize")]) + }, + "strconv.FormatInt": func(call *callcheck.Call) { + validateIntBase(call.Args[knowledge.Arg("strconv.FormatInt.base")]) + }, + "strconv.FormatUint": func(call *callcheck.Call) { + validateIntBase(call.Args[knowledge.Arg("strconv.FormatUint.base")]) + }, + + "strconv.AppendFloat": func(call *callcheck.Call) { + validateFloatFormat(call.Args[knowledge.Arg("strconv.AppendFloat.fmt")]) + validateFloatBitSize(call.Args[knowledge.Arg("strconv.AppendFloat.bitSize")]) + }, + "strconv.AppendInt": func(call *callcheck.Call) { + validateIntBase(call.Args[knowledge.Arg("strconv.AppendInt.base")]) + }, + "strconv.AppendUint": func(call *callcheck.Call) { + validateIntBase(call.Args[knowledge.Arg("strconv.AppendUint.base")]) + }, +} + +func validateDiscreetBitSize(arg *callcheck.Argument, size1 int, size2 int) { + if c := callcheck.ExtractConstExpectKind(arg.Value, constant.Int); c != nil { + val, _ := constant.Int64Val(c.Value) + if val != int64(size1) && val != int64(size2) { + arg.Invalid(fmt.Sprintf("'bitSize' argument is invalid, must be either %d or %d", size1, size2)) + } + } +} + +func validateComplexBitSize(arg *callcheck.Argument) { validateDiscreetBitSize(arg, 64, 128) } +func validateFloatBitSize(arg *callcheck.Argument) { validateDiscreetBitSize(arg, 32, 64) } + +func validateContinuousBitSize(arg *callcheck.Argument, min int, max int) { + if c := callcheck.ExtractConstExpectKind(arg.Value, constant.Int); c != nil { + val, _ := constant.Int64Val(c.Value) + if val < int64(min) || val > int64(max) { + arg.Invalid(fmt.Sprintf("'bitSize' argument is invalid, must be within %d and %d", min, max)) + } + } +} + +func validateIntBase(arg *callcheck.Argument) { + if c := callcheck.ExtractConstExpectKind(arg.Value, constant.Int); c != nil { + val, _ := constant.Int64Val(c.Value) + if val < 2 { + arg.Invalid("'base' must not be smaller than 2") + } + if val > 36 { + arg.Invalid("'base' must not be larger than 36") + } + } +} + +func validateIntBaseAllowZero(arg *callcheck.Argument) { + if c := callcheck.ExtractConstExpectKind(arg.Value, constant.Int); c != nil { + val, _ := constant.Int64Val(c.Value) + if val < 2 && val != 0 { + arg.Invalid("'base' must not be smaller than 2, unless it is 0") + } + if val > 36 { + arg.Invalid("'base' must not be larger than 36") + } + } +} + +func validateComplexFormat(arg *callcheck.Argument) { + validateFloatFormat(arg) +} + +func validateFloatFormat(arg *callcheck.Argument) { + if c := callcheck.ExtractConstExpectKind(arg.Value, constant.Int); c != nil { + val, _ := constant.Int64Val(c.Value) + switch val { + case 'b', 'e', 'E', 'f', 'g', 'G', 'x', 'X': + default: + arg.Invalid(fmt.Sprintf("'fmt' argument is invalid: unknown format %q", val)) + } + } +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa1031/sa1031.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa1031/sa1031.go new file mode 100644 index 0000000000..52ba4dcda2 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa1031/sa1031.go @@ -0,0 +1,81 @@ +package sa1031 + +import ( + "go/constant" + "go/token" + + "honnef.co/go/tools/analysis/callcheck" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/go/ir" + "honnef.co/go/tools/go/ir/irutil" + "honnef.co/go/tools/internal/passes/buildir" + "honnef.co/go/tools/knowledge" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA1031", + Requires: []*analysis.Analyzer{buildir.Analyzer}, + Run: callcheck.Analyzer(checkEncodeRules), + }, + Doc: &lint.RawDocumentation{ + Title: `Overlapping byte slices passed to an encoder`, + Text: `In an encoding function of the form \'Encode(dst, src)\', \'dst\' and +\'src\' were found to reference the same memory. This can result in +\'src\' bytes being overwritten before they are read, when the encoder +writes more than one byte per \'src\' byte.`, + Since: "2024.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var checkEncodeRules = map[string]callcheck.Check{ + "encoding/ascii85.Encode": checkNonOverlappingDstSrc(knowledge.Arg("encoding/ascii85.Encode.dst"), knowledge.Arg("encoding/ascii85.Encode.src")), + "(*encoding/base32.Encoding).Encode": checkNonOverlappingDstSrc(knowledge.Arg("(*encoding/base32.Encoding).Encode.dst"), knowledge.Arg("(*encoding/base32.Encoding).Encode.src")), + "(*encoding/base64.Encoding).Encode": checkNonOverlappingDstSrc(knowledge.Arg("(*encoding/base64.Encoding).Encode.dst"), knowledge.Arg("(*encoding/base64.Encoding).Encode.src")), + "encoding/hex.Encode": checkNonOverlappingDstSrc(knowledge.Arg("encoding/hex.Encode.dst"), knowledge.Arg("encoding/hex.Encode.src")), +} + +func checkNonOverlappingDstSrc(dstArg, srcArg int) callcheck.Check { + return func(call *callcheck.Call) { + dst := call.Args[dstArg] + src := call.Args[srcArg] + _, dstConst := irutil.Flatten(dst.Value.Value).(*ir.Const) + _, srcConst := irutil.Flatten(src.Value.Value).(*ir.Const) + if dstConst || srcConst { + // one of the arguments is nil, therefore overlap is not possible + return + } + if dst.Value == src.Value { + // simple case of f(b, b) + dst.Invalid("overlapping dst and src") + return + } + dstSlice, ok := irutil.Flatten(dst.Value.Value).(*ir.Slice) + if !ok { + return + } + srcSlice, ok := irutil.Flatten(src.Value.Value).(*ir.Slice) + if !ok { + return + } + if irutil.Flatten(dstSlice.X) != irutil.Flatten(srcSlice.X) { + // differing underlying arrays, all is well + return + } + l1 := irutil.Flatten(dstSlice.Low) + l2 := irutil.Flatten(srcSlice.Low) + c1, ok1 := l1.(*ir.Const) + c2, ok2 := l2.(*ir.Const) + if l1 == l2 || (ok1 && ok2 && constant.Compare(c1.Value, token.EQL, c2.Value)) { + // dst and src are the same slice, and have the same lower bound + dst.Invalid("overlapping dst and src") + return + } + } +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa1032/sa1032.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa1032/sa1032.go new file mode 100644 index 0000000000..2ea6038613 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa1032/sa1032.go @@ -0,0 +1,79 @@ +package sa1032 + +import ( + "honnef.co/go/tools/analysis/callcheck" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/go/ir" + "honnef.co/go/tools/internal/passes/buildir" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA1032", + Requires: []*analysis.Analyzer{buildir.Analyzer}, + Run: callcheck.Analyzer(rules), + }, + Doc: &lint.RawDocumentation{ + Title: `Wrong order of arguments to \'errors.Is\'`, + Text: ` +The first argument of the function \'errors.Is\' is the error +that we have and the second argument is the error we're trying to match against. +For example: + + if errors.Is(err, io.EOF) { ... } + +This check detects some cases where the two arguments have been swapped. It +flags any calls where the first argument is referring to a package-level error +variable, such as + + if errors.Is(io.EOF, err) { /* this is wrong */ }`, + Since: "2024.1", + Severity: lint.SeverityError, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var rules = map[string]callcheck.Check{ + "errors.Is": validateIs, +} + +func validateIs(call *callcheck.Call) { + if len(call.Args) != 2 { + return + } + + global := func(arg *callcheck.Argument) *ir.Global { + v, ok := arg.Value.Value.(*ir.Load) + if !ok { + return nil + } + g, _ := v.X.(*ir.Global) + return g + } + + x, y := call.Args[0], call.Args[1] + gx := global(x) + if gx == nil { + return + } + + if pkgx := gx.Package().Pkg; pkgx != nil && pkgx.Path() != call.Pass.Pkg.Path() { + // x is a global that's not in this package + + if gy := global(y); gy != nil { + if pkgy := gy.Package().Pkg; pkgy != nil && pkgy.Path() != call.Pass.Pkg.Path() { + // Both arguments refer to globals that aren't in this package. This can + // genuinely happen for external tests that check that one error "is" + // another one. net/http's external tests, for example, do + // `errors.Is(http.ErrNotSupported, errors.ErrUnsupported)`. + return + } + } + + call.Invalid("arguments have the wrong order") + } +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa2000/sa2000.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa2000/sa2000.go new file mode 100644 index 0000000000..2de4f5626e --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa2000/sa2000.go @@ -0,0 +1,48 @@ +package sa2000 + +import ( + "fmt" + "go/ast" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA2000", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `\'sync.WaitGroup.Add\' called inside the goroutine, leading to a race condition`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var checkWaitgroupAddQ = pattern.MustParse(` + (GoStmt + (CallExpr + (FuncLit + _ + call@(CallExpr (Symbol "(*sync.WaitGroup).Add") _):_) _))`) + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + if m, ok := code.Match(pass, checkWaitgroupAddQ, node); ok { + call := m.State["call"].(ast.Node) + report.Report(pass, call, fmt.Sprintf("should call %s before starting the goroutine to avoid a race", report.Render(pass, call))) + } + } + code.Preorder(pass, fn, (*ast.GoStmt)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa2001/sa2001.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa2001/sa2001.go new file mode 100644 index 0000000000..21ca99c61c --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa2001/sa2001.go @@ -0,0 +1,111 @@ +package sa2001 + +import ( + "go/ast" + "go/types" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/ast/astutil" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA2001", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Empty critical section, did you mean to defer the unlock?`, + Text: `Empty critical sections of the kind + + mu.Lock() + mu.Unlock() + +are very often a typo, and the following was intended instead: + + mu.Lock() + defer mu.Unlock() + +Do note that sometimes empty critical sections can be useful, as a +form of signaling to wait on another goroutine. Many times, there are +simpler ways of achieving the same effect. When that isn't the case, +the code should be amply commented to avoid confusion. Combining such +comments with a \'//lint:ignore\' directive can be used to suppress this +rare false positive.`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + if pass.Pkg.Path() == "sync_test" { + // exception for the sync package's tests + return nil, nil + } + + // Initially it might seem like this check would be easier to + // implement using IR. After all, we're only checking for two + // consecutive method calls. In reality, however, there may be any + // number of other instructions between the lock and unlock, while + // still constituting an empty critical section. For example, + // given `m.x().Lock(); m.x().Unlock()`, there will be a call to + // x(). In the AST-based approach, this has a tiny potential for a + // false positive (the second call to x might be doing work that + // is protected by the mutex). In an IR-based approach, however, + // it would miss a lot of real bugs. + + mutexParams := func(s ast.Stmt) (x ast.Expr, funcName string, ok bool) { + expr, ok := s.(*ast.ExprStmt) + if !ok { + return nil, "", false + } + call, ok := astutil.Unparen(expr.X).(*ast.CallExpr) + if !ok { + return nil, "", false + } + sel, ok := call.Fun.(*ast.SelectorExpr) + if !ok { + return nil, "", false + } + + fn, ok := pass.TypesInfo.ObjectOf(sel.Sel).(*types.Func) + if !ok { + return nil, "", false + } + sig := fn.Type().(*types.Signature) + if sig.Params().Len() != 0 || sig.Results().Len() != 0 { + return nil, "", false + } + + return sel.X, fn.Name(), true + } + + fn := func(node ast.Node) { + block := node.(*ast.BlockStmt) + if len(block.List) < 2 { + return + } + for i := range block.List[:len(block.List)-1] { + sel1, method1, ok1 := mutexParams(block.List[i]) + sel2, method2, ok2 := mutexParams(block.List[i+1]) + + if !ok1 || !ok2 || report.Render(pass, sel1) != report.Render(pass, sel2) { + continue + } + if (method1 == "Lock" && method2 == "Unlock") || + (method1 == "RLock" && method2 == "RUnlock") { + report.Report(pass, block.List[i+1], "empty critical section") + } + } + } + code.Preorder(pass, fn, (*ast.BlockStmt)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa2002/sa2002.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa2002/sa2002.go new file mode 100644 index 0000000000..1f3eefaddd --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa2002/sa2002.go @@ -0,0 +1,93 @@ +package sa2002 + +import ( + "fmt" + "go/types" + + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/ir" + "honnef.co/go/tools/go/types/typeutil" + "honnef.co/go/tools/internal/passes/buildir" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA2002", + Run: run, + Requires: []*analysis.Analyzer{buildir.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Called \'testing.T.FailNow\' or \'SkipNow\' in a goroutine, which isn't allowed`, + Since: "2017.1", + Severity: lint.SeverityError, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { + for _, block := range fn.Blocks { + for _, ins := range block.Instrs { + gostmt, ok := ins.(*ir.Go) + if !ok { + continue + } + var fn *ir.Function + switch val := gostmt.Call.Value.(type) { + case *ir.Function: + fn = val + case *ir.MakeClosure: + fn = val.Fn.(*ir.Function) + default: + continue + } + if fn.Blocks == nil { + continue + } + for _, block := range fn.Blocks { + for _, ins := range block.Instrs { + call, ok := ins.(*ir.Call) + if !ok { + continue + } + if call.Call.IsInvoke() { + continue + } + callee := call.Call.StaticCallee() + if callee == nil { + continue + } + recv := callee.Signature.Recv() + if recv == nil { + continue + } + if !typeutil.IsPointerToTypeWithName(recv.Type(), "testing.common") { + continue + } + fn, ok := call.Call.StaticCallee().Object().(*types.Func) + if !ok { + continue + } + name := fn.Name() + switch name { + case "FailNow", "Fatal", "Fatalf", "SkipNow", "Skip", "Skipf": + default: + continue + } + // TODO(dh): don't report multiple diagnostics + // for multiple calls to T.Fatal, but do + // collect all of them as related information + report.Report(pass, gostmt, fmt.Sprintf("the goroutine calls T.%s, which must be called in the same goroutine as the test", name), + report.Related(call, fmt.Sprintf("call to T.%s", name))) + } + } + } + } + } + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa2003/sa2003.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa2003/sa2003.go new file mode 100644 index 0000000000..9085d30520 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa2003/sa2003.go @@ -0,0 +1,87 @@ +package sa2003 + +import ( + "fmt" + "go/types" + + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/ir" + "honnef.co/go/tools/go/ir/irutil" + "honnef.co/go/tools/internal/passes/buildir" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA2003", + Run: run, + Requires: []*analysis.Analyzer{buildir.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Deferred \'Lock\' right after locking, likely meant to defer \'Unlock\' instead`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { + for _, block := range fn.Blocks { + instrs := irutil.FilterDebug(block.Instrs) + if len(instrs) < 2 { + continue + } + for i, ins := range instrs[:len(instrs)-1] { + call, ok := ins.(*ir.Call) + if !ok { + continue + } + if !irutil.IsCallToAny(call.Common(), "(*sync.Mutex).Lock", "(*sync.RWMutex).RLock") { + continue + } + nins, ok := instrs[i+1].(*ir.Defer) + if !ok { + continue + } + if !irutil.IsCallToAny(&nins.Call, "(*sync.Mutex).Lock", "(*sync.RWMutex).RLock") { + continue + } + if call.Common().Args[0] != nins.Call.Args[0] { + continue + } + name := shortCallName(call.Common()) + alt := "" + switch name { + case "Lock": + alt = "Unlock" + case "RLock": + alt = "RUnlock" + } + report.Report(pass, nins, fmt.Sprintf("deferring %s right after having locked already; did you mean to defer %s?", name, alt)) + } + } + } + return nil, nil +} + +func shortCallName(call *ir.CallCommon) string { + if call.IsInvoke() { + return "" + } + switch v := call.Value.(type) { + case *ir.Function: + fn, ok := v.Object().(*types.Func) + if !ok { + return "" + } + return fn.Name() + case *ir.Builtin: + return v.Name() + } + return "" +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa3000/sa3000.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa3000/sa3000.go new file mode 100644 index 0000000000..2f11060658 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa3000/sa3000.go @@ -0,0 +1,118 @@ +package sa3000 + +import ( + "go/ast" + "go/types" + "go/version" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/inspector" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA3000", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `\'TestMain\' doesn't call \'os.Exit\', hiding test failures`, + Text: `Test executables (and in turn \"go test\") exit with a non-zero status +code if any tests failed. When specifying your own \'TestMain\' function, +it is your responsibility to arrange for this, by calling \'os.Exit\' with +the correct code. The correct code is returned by \'(*testing.M).Run\', so +the usual way of implementing \'TestMain\' is to end it with +\'os.Exit(m.Run())\'.`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + var ( + fnmain ast.Node + callsExit bool + callsRun bool + arg types.Object + ) + fn := func(node ast.Node, push bool) bool { + if !push { + if fnmain != nil && node == fnmain { + if !callsExit && callsRun { + report.Report(pass, fnmain, "TestMain should call os.Exit to set exit code") + } + fnmain = nil + callsExit = false + callsRun = false + arg = nil + } + return true + } + + switch node := node.(type) { + case *ast.FuncDecl: + if fnmain != nil { + return true + } + if !isTestMain(pass, node) { + return false + } + if version.Compare(code.StdlibVersion(pass, node), "go1.15") >= 0 { + // Beginning with Go 1.15, the test framework will call + // os.Exit for us. + return false + } + fnmain = node + arg = pass.TypesInfo.ObjectOf(node.Type.Params.List[0].Names[0]) + return true + case *ast.CallExpr: + if code.IsCallTo(pass, node, "os.Exit") { + callsExit = true + return false + } + sel, ok := node.Fun.(*ast.SelectorExpr) + if !ok { + return true + } + ident, ok := sel.X.(*ast.Ident) + if !ok { + return true + } + if arg != pass.TypesInfo.ObjectOf(ident) { + return true + } + if sel.Sel.Name == "Run" { + callsRun = true + return false + } + return true + default: + lint.ExhaustiveTypeSwitch(node) + return true + } + } + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Nodes([]ast.Node{(*ast.FuncDecl)(nil), (*ast.CallExpr)(nil)}, fn) + return nil, nil +} + +func isTestMain(pass *analysis.Pass, decl *ast.FuncDecl) bool { + if decl.Name.Name != "TestMain" { + return false + } + if len(decl.Type.Params.List) != 1 { + return false + } + arg := decl.Type.Params.List[0] + if len(arg.Names) != 1 { + return false + } + return code.IsOfPointerToTypeWithName(pass, arg.Type, "testing.M") +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa3001/sa3001.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa3001/sa3001.go new file mode 100644 index 0000000000..c51f179e3d --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa3001/sa3001.go @@ -0,0 +1,55 @@ +package sa3001 + +import ( + "fmt" + "go/ast" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA3001", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Assigning to \'b.N\' in benchmarks distorts the results`, + Text: `The testing package dynamically sets \'b.N\' to improve the reliability of +benchmarks and uses it in computations to determine the duration of a +single operation. Benchmark code must not alter \'b.N\' as this would +falsify results.`, + Since: "2017.1", + Severity: lint.SeverityError, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + assign := node.(*ast.AssignStmt) + if len(assign.Lhs) != 1 || len(assign.Rhs) != 1 { + return + } + sel, ok := assign.Lhs[0].(*ast.SelectorExpr) + if !ok { + return + } + if sel.Sel.Name != "N" { + return + } + if !code.IsOfPointerToTypeWithName(pass, sel.X, "testing.B") { + return + } + report.Report(pass, assign, fmt.Sprintf("should not assign to %s", report.Render(pass, sel))) + } + code.Preorder(pass, fn, (*ast.AssignStmt)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa4000/sa4000.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa4000/sa4000.go new file mode 100644 index 0000000000..577bc3938e --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa4000/sa4000.go @@ -0,0 +1,146 @@ +package sa4000 + +import ( + "fmt" + "go/ast" + "go/token" + "go/types" + "reflect" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/types/typeutil" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA4000", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Binary operator has identical expressions on both sides`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + var isFloat func(T types.Type) bool + isFloat = func(T types.Type) bool { + tset := typeutil.NewTypeSet(T) + if len(tset.Terms) == 0 { + // no terms, so floats are a possibility + return true + } + return tset.Any(func(term *types.Term) bool { + switch typ := term.Type().Underlying().(type) { + case *types.Basic: + kind := typ.Kind() + return kind == types.Float32 || kind == types.Float64 + case *types.Array: + return isFloat(typ.Elem()) + case *types.Struct: + for i := 0; i < typ.NumFields(); i++ { + if !isFloat(typ.Field(i).Type()) { + return false + } + } + return true + default: + return false + } + }) + } + + // TODO(dh): this check ignores the existence of side-effects and + // happily flags fn() == fn() – so far, we've had nobody complain + // about a false positive, and it's caught several bugs in real + // code. + // + // We special case functions from the math/rand package. Someone ran + // into the following false positive: "rand.Intn(2) - rand.Intn(2), which I wrote to generate values {-1, 0, 1} with {0.25, 0.5, 0.25} probability." + fn := func(node ast.Node) { + op := node.(*ast.BinaryExpr) + switch op.Op { + case token.EQL, token.NEQ: + case token.SUB, token.QUO, token.AND, token.REM, token.OR, token.XOR, token.AND_NOT, + token.LAND, token.LOR, token.LSS, token.GTR, token.LEQ, token.GEQ: + default: + // For some ops, such as + and *, it can make sense to + // have identical operands + return + } + + if isFloat(pass.TypesInfo.TypeOf(op.X)) { + // 'float float' makes sense for several operators. + // We've tried keeping an exact list of operators to allow, but floats keep surprising us. Let's just give up instead. + return + } + + if reflect.TypeOf(op.X) != reflect.TypeOf(op.Y) { + return + } + if report.Render(pass, op.X) != report.Render(pass, op.Y) { + return + } + l1, ok1 := op.X.(*ast.BasicLit) + l2, ok2 := op.Y.(*ast.BasicLit) + if ok1 && ok2 && l1.Kind == token.INT && l2.Kind == l1.Kind && l1.Value == "0" && l2.Value == l1.Value && code.IsGenerated(pass, l1.Pos()) { + // cgo generates the following function call: + // _cgoCheckPointer(_cgoBase0, 0 == 0) – it uses 0 == 0 + // instead of true in case the user shadowed the + // identifier. Ideally we'd restrict this exception to + // calls of _cgoCheckPointer, but it's not worth the + // hassle of keeping track of the stack. + // are very rare to begin with, and we're mostly checking + // for them to catch typos such as 1 == 1 where the user + // meant to type i == 1. The odds of a false negative for + // 0 == 0 are slim. + return + } + + if expr, ok := op.X.(*ast.CallExpr); ok { + call := code.CallName(pass, expr) + switch call { + case "math/rand.Int", + "math/rand.Int31", + "math/rand.Int31n", + "math/rand.Int63", + "math/rand.Int63n", + "math/rand.Intn", + "math/rand.Uint32", + "math/rand.Uint64", + "math/rand.ExpFloat64", + "math/rand.Float32", + "math/rand.Float64", + "math/rand.NormFloat64", + "(*math/rand.Rand).Int", + "(*math/rand.Rand).Int31", + "(*math/rand.Rand).Int31n", + "(*math/rand.Rand).Int63", + "(*math/rand.Rand).Int63n", + "(*math/rand.Rand).Intn", + "(*math/rand.Rand).Uint32", + "(*math/rand.Rand).Uint64", + "(*math/rand.Rand).ExpFloat64", + "(*math/rand.Rand).Float32", + "(*math/rand.Rand).Float64", + "(*math/rand.Rand).NormFloat64": + return + } + } + + report.Report(pass, op, fmt.Sprintf("identical expressions on the left and right side of the '%s' operator", op.Op)) + } + code.Preorder(pass, fn, (*ast.BinaryExpr)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa4001/sa4001.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa4001/sa4001.go new file mode 100644 index 0000000000..63e527f6ae --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa4001/sa4001.go @@ -0,0 +1,52 @@ +package sa4001 + +import ( + "go/ast" + "regexp" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA4001", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `\'&*x\' gets simplified to \'x\', it does not copy \'x\'`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var ( + // cgo produces code like fn(&*_Cvar_kSomeCallbacks) which we don't + // want to flag. + cgoIdent = regexp.MustCompile(`^_C(func|var)_.+$`) + checkIneffectiveCopyQ1 = pattern.MustParse(`(UnaryExpr "&" (StarExpr obj))`) + checkIneffectiveCopyQ2 = pattern.MustParse(`(StarExpr (UnaryExpr "&" _))`) +) + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + if m, ok := code.Match(pass, checkIneffectiveCopyQ1, node); ok { + if ident, ok := m.State["obj"].(*ast.Ident); !ok || !cgoIdent.MatchString(ident.Name) { + report.Report(pass, node, "&*x will be simplified to x. It will not copy x.") + } + } else if _, ok := code.Match(pass, checkIneffectiveCopyQ2, node); ok { + report.Report(pass, node, "*&x will be simplified to x. It will not copy x.") + } + } + code.Preorder(pass, fn, (*ast.UnaryExpr)(nil), (*ast.StarExpr)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa4003/sa4003.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa4003/sa4003.go new file mode 100644 index 0000000000..033ee3a6c9 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa4003/sa4003.go @@ -0,0 +1,159 @@ +package sa4003 + +import ( + "fmt" + "go/ast" + "go/constant" + "go/token" + "go/types" + "math" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/types/typeutil" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA4003", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Comparing unsigned values against negative values is pointless`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAll, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + isobj := func(expr ast.Expr, name string) bool { + if name == "" { + return false + } + sel, ok := expr.(*ast.SelectorExpr) + if !ok { + return false + } + return typeutil.IsObject(pass.TypesInfo.ObjectOf(sel.Sel), name) + } + + fn := func(node ast.Node) { + expr := node.(*ast.BinaryExpr) + tx := pass.TypesInfo.TypeOf(expr.X) + basic, ok := tx.Underlying().(*types.Basic) + if !ok { + return + } + + // We only check for the math constants and integer literals, not for all constant expressions. This is to avoid + // false positives when constant values differ under different build tags. + var ( + maxMathConst string + minMathConst string + maxLiteral constant.Value + minLiteral constant.Value + ) + + switch basic.Kind() { + case types.Uint8: + maxMathConst = "math.MaxUint8" + minLiteral = constant.MakeUint64(0) + maxLiteral = constant.MakeUint64(math.MaxUint8) + case types.Uint16: + maxMathConst = "math.MaxUint16" + minLiteral = constant.MakeUint64(0) + maxLiteral = constant.MakeUint64(math.MaxUint16) + case types.Uint32: + maxMathConst = "math.MaxUint32" + minLiteral = constant.MakeUint64(0) + maxLiteral = constant.MakeUint64(math.MaxUint32) + case types.Uint64: + maxMathConst = "math.MaxUint64" + minLiteral = constant.MakeUint64(0) + maxLiteral = constant.MakeUint64(math.MaxUint64) + case types.Uint: + // TODO(dh): we could chose 32 bit vs 64 bit depending on the file's build tags + maxMathConst = "math.MaxUint64" + minLiteral = constant.MakeUint64(0) + maxLiteral = constant.MakeUint64(math.MaxUint64) + + case types.Int8: + minMathConst = "math.MinInt8" + maxMathConst = "math.MaxInt8" + minLiteral = constant.MakeInt64(math.MinInt8) + maxLiteral = constant.MakeInt64(math.MaxInt8) + case types.Int16: + minMathConst = "math.MinInt16" + maxMathConst = "math.MaxInt16" + minLiteral = constant.MakeInt64(math.MinInt16) + maxLiteral = constant.MakeInt64(math.MaxInt16) + case types.Int32: + minMathConst = "math.MinInt32" + maxMathConst = "math.MaxInt32" + minLiteral = constant.MakeInt64(math.MinInt32) + maxLiteral = constant.MakeInt64(math.MaxInt32) + case types.Int64: + minMathConst = "math.MinInt64" + maxMathConst = "math.MaxInt64" + minLiteral = constant.MakeInt64(math.MinInt64) + maxLiteral = constant.MakeInt64(math.MaxInt64) + case types.Int: + // TODO(dh): we could chose 32 bit vs 64 bit depending on the file's build tags + minMathConst = "math.MinInt64" + maxMathConst = "math.MaxInt64" + minLiteral = constant.MakeInt64(math.MinInt64) + maxLiteral = constant.MakeInt64(math.MaxInt64) + } + + isLiteral := func(expr ast.Expr, c constant.Value) bool { + if c == nil { + return false + } + return code.IsIntegerLiteral(pass, expr, c) + } + isZeroLiteral := func(expr ast.Expr) bool { + return code.IsIntegerLiteral(pass, expr, constant.MakeInt64(0)) + } + + if (expr.Op == token.GTR || expr.Op == token.GEQ) && (isobj(expr.Y, maxMathConst) || isLiteral(expr.Y, maxLiteral)) || + (expr.Op == token.LSS || expr.Op == token.LEQ) && (isobj(expr.X, maxMathConst) || isLiteral(expr.X, maxLiteral)) { + report.Report(pass, expr, fmt.Sprintf("no value of type %s is greater than %s", basic, maxMathConst)) + } + + if expr.Op == token.LEQ && (isobj(expr.Y, maxMathConst) || isLiteral(expr.Y, maxLiteral)) || + expr.Op == token.GEQ && (isobj(expr.X, maxMathConst) || isLiteral(expr.X, maxLiteral)) { + report.Report(pass, expr, fmt.Sprintf("every value of type %s is <= %s", basic, maxMathConst)) + } + + if (basic.Info() & types.IsUnsigned) != 0 { + if (expr.Op == token.LSS && isZeroLiteral(expr.Y)) || + (expr.Op == token.GTR && isZeroLiteral(expr.X)) { + report.Report(pass, expr, fmt.Sprintf("no value of type %s is less than 0", basic)) + } + if expr.Op == token.GEQ && isZeroLiteral(expr.Y) || + expr.Op == token.LEQ && isZeroLiteral(expr.X) { + report.Report(pass, expr, fmt.Sprintf("every value of type %s is >= 0", basic)) + } + } else { + if (expr.Op == token.LSS || expr.Op == token.LEQ) && (isobj(expr.Y, minMathConst) || isLiteral(expr.Y, minLiteral)) || + (expr.Op == token.GTR || expr.Op == token.GEQ) && (isobj(expr.X, minMathConst) || isLiteral(expr.X, minLiteral)) { + report.Report(pass, expr, fmt.Sprintf("no value of type %s is less than %s", basic, minMathConst)) + } + if expr.Op == token.GEQ && (isobj(expr.Y, minMathConst) || isLiteral(expr.Y, minLiteral)) || + expr.Op == token.LEQ && (isobj(expr.X, minMathConst) || isLiteral(expr.X, minLiteral)) { + report.Report(pass, expr, fmt.Sprintf("every value of type %s is >= %s", basic, minMathConst)) + } + } + + } + code.Preorder(pass, fn, (*ast.BinaryExpr)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa4004/sa4004.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa4004/sa4004.go new file mode 100644 index 0000000000..e38f9f3eea --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa4004/sa4004.go @@ -0,0 +1,159 @@ +package sa4004 + +import ( + "go/ast" + "go/token" + "go/types" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/types/typeutil" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA4004", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `The loop exits unconditionally after one iteration`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAll, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + // This check detects some, but not all unconditional loop exits. + // We give up in the following cases: + // + // - a goto anywhere in the loop. The goto might skip over our + // return, and we don't check that it doesn't. + // + // - any nested, unlabelled continue, even if it is in another + // loop or closure. + fn := func(node ast.Node) { + var body *ast.BlockStmt + switch fn := node.(type) { + case *ast.FuncDecl: + body = fn.Body + case *ast.FuncLit: + body = fn.Body + default: + lint.ExhaustiveTypeSwitch(node) + } + if body == nil { + return + } + labels := map[types.Object]ast.Stmt{} + ast.Inspect(body, func(node ast.Node) bool { + label, ok := node.(*ast.LabeledStmt) + if !ok { + return true + } + labels[pass.TypesInfo.ObjectOf(label.Label)] = label.Stmt + return true + }) + + ast.Inspect(body, func(node ast.Node) bool { + var loop ast.Node + var body *ast.BlockStmt + switch node := node.(type) { + case *ast.ForStmt: + body = node.Body + loop = node + case *ast.RangeStmt: + ok := typeutil.All(pass.TypesInfo.TypeOf(node.X), func(term *types.Term) bool { + switch term.Type().Underlying().(type) { + case *types.Slice, *types.Chan, *types.Basic, *types.Pointer, *types.Array: + return true + case *types.Map: + // looping once over a map is a valid pattern for + // getting an arbitrary element. + return false + case *types.Signature: + // we have no idea what semantics the function implements + return false + default: + lint.ExhaustiveTypeSwitch(term.Type().Underlying()) + return false + } + }) + if !ok { + return true + } + body = node.Body + loop = node + default: + return true + } + if len(body.List) < 2 { + // TODO(dh): is this check needed? when body.List < 2, + // then we can't find both an unconditional exit and a + // branching statement (if, ...). and we don't flag + // unconditional exits if there has been no branching + // in the loop body. + + // avoid flagging the somewhat common pattern of using + // a range loop to get the first element in a slice, + // or the first rune in a string. + return true + } + var unconditionalExit ast.Node + hasBranching := false + for _, stmt := range body.List { + switch stmt := stmt.(type) { + case *ast.BranchStmt: + switch stmt.Tok { + case token.BREAK: + if stmt.Label == nil || labels[pass.TypesInfo.ObjectOf(stmt.Label)] == loop { + unconditionalExit = stmt + } + case token.CONTINUE: + if stmt.Label == nil || labels[pass.TypesInfo.ObjectOf(stmt.Label)] == loop { + unconditionalExit = nil + return false + } + } + case *ast.ReturnStmt: + unconditionalExit = stmt + case *ast.IfStmt, *ast.ForStmt, *ast.RangeStmt, *ast.SwitchStmt, *ast.SelectStmt: + hasBranching = true + } + } + if unconditionalExit == nil || !hasBranching { + return false + } + ast.Inspect(body, func(node ast.Node) bool { + if branch, ok := node.(*ast.BranchStmt); ok { + + switch branch.Tok { + case token.GOTO: + unconditionalExit = nil + return false + case token.CONTINUE: + if branch.Label != nil && labels[pass.TypesInfo.ObjectOf(branch.Label)] != loop { + return true + } + unconditionalExit = nil + return false + } + } + return true + }) + if unconditionalExit != nil { + report.Report(pass, unconditionalExit, "the surrounding loop is unconditionally terminated") + } + return true + }) + } + code.Preorder(pass, fn, (*ast.FuncDecl)(nil), (*ast.FuncLit)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa4005/sa4005.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa4005/sa4005.go new file mode 100644 index 0000000000..fc1d78c8c4 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa4005/sa4005.go @@ -0,0 +1,138 @@ +package sa4005 + +import ( + "fmt" + "go/types" + + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/ir" + "honnef.co/go/tools/go/ir/irutil" + "honnef.co/go/tools/internal/passes/buildir" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA4005", + Run: run, + Requires: []*analysis.Analyzer{buildir.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Field assignment that will never be observed. Did you mean to use a pointer receiver?`, + Since: "2021.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + // The analysis only considers the receiver and its first level + // fields. It doesn't look at other parameters, nor at nested + // fields. + // + // The analysis does not detect all kinds of dead stores, only + // those of fields that are never read after the write. That is, + // we do not flag 'a.x = 1; a.x = 2; _ = a.x'. We might explore + // this again if we add support for SROA to go/ir and implement + // https://github.com/dominikh/go-tools/issues/191. + + irpkg := pass.ResultOf[buildir.Analyzer].(*buildir.IR) +fnLoop: + for _, fn := range irpkg.SrcFuncs { + if recv := fn.Signature.Recv(); recv == nil { + continue + } else if _, ok := recv.Type().Underlying().(*types.Struct); !ok { + continue + } + + recv := fn.Params[0] + refs := irutil.FilterDebug(*recv.Referrers()) + if len(refs) != 1 { + continue + } + store, ok := refs[0].(*ir.Store) + if !ok { + continue + } + alloc, ok := store.Addr.(*ir.Alloc) + if !ok || alloc.Heap { + continue + } + + reads := map[int][]ir.Instruction{} + writes := map[int][]ir.Instruction{} + for _, ref := range *alloc.Referrers() { + switch ref := ref.(type) { + case *ir.FieldAddr: + for _, refref := range *ref.Referrers() { + switch refref.(type) { + case *ir.Store: + writes[ref.Field] = append(writes[ref.Field], refref) + case *ir.Load: + reads[ref.Field] = append(reads[ref.Field], refref) + case *ir.DebugRef: + continue + default: + // this should be safe… if the field address + // escapes, then alloc.Heap will be true. + // there should be no instructions left that, + // given this FieldAddr, without escaping, can + // effect a load or store. + continue + } + } + case *ir.Store: + // we could treat this as a store to every field, but + // we don't want to decide the semantics of partial + // struct initializers. should `v = t{x: 1}` also mark + // v.y as being written to? + if ref != store { + continue fnLoop + } + case *ir.Load: + // a load of the entire struct loads every field + for i := 0; i < recv.Type().Underlying().(*types.Struct).NumFields(); i++ { + reads[i] = append(reads[i], ref) + } + case *ir.DebugRef: + continue + default: + continue fnLoop + } + } + + offset := func(instr ir.Instruction) int { + for i, other := range instr.Block().Instrs { + if instr == other { + return i + } + } + panic("couldn't find instruction in its block") + } + + for field, ws := range writes { + rs := reads[field] + wLoop: + for _, w := range ws { + for _, r := range rs { + if w.Block() == r.Block() { + if offset(r) > offset(w) { + // found a reachable read of our write + continue wLoop + } + } else if irutil.Reachable(w.Block(), r.Block()) { + // found a reachable read of our write + continue wLoop + } + } + fieldName := recv.Type().Underlying().(*types.Struct).Field(field).Name() + report.Report(pass, w, fmt.Sprintf("ineffective assignment to field %s.%s", recv.Type().(interface{ Obj() *types.TypeName }).Obj().Name(), fieldName)) + } + } + } + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa4006/sa4006.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa4006/sa4006.go new file mode 100644 index 0000000000..e980434a43 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa4006/sa4006.go @@ -0,0 +1,155 @@ +package sa4006 + +import ( + "fmt" + "go/ast" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/ir" + "honnef.co/go/tools/go/ir/irutil" + "honnef.co/go/tools/internal/passes/buildir" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA4006", + Run: run, + Requires: []*analysis.Analyzer{buildir.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `A value assigned to a variable is never read before being overwritten. Forgotten error check or dead code?`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAll, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { + if irutil.IsExample(fn) { + continue + } + node := fn.Source() + if node == nil { + continue + } + if gen, ok := code.Generator(pass, node.Pos()); ok && gen == generated.Goyacc { + // Don't flag unused values in code generated by goyacc. + // There may be hundreds of those due to the way the state + // machine is constructed. + continue + } + + switchTags := map[ir.Value]struct{}{} + ast.Inspect(node, func(node ast.Node) bool { + s, ok := node.(*ast.SwitchStmt) + if !ok { + return true + } + v, _ := fn.ValueForExpr(s.Tag) + switchTags[v] = struct{}{} + return true + }) + + // OPT(dh): don't use a map, possibly use a bitset + var hasUse func(v ir.Value, seen map[ir.Value]struct{}) bool + hasUse = func(v ir.Value, seen map[ir.Value]struct{}) bool { + if _, ok := seen[v]; ok { + return false + } + if _, ok := switchTags[v]; ok { + return true + } + refs := v.Referrers() + if refs == nil { + // TODO investigate why refs can be nil + return true + } + for _, ref := range *refs { + switch ref := ref.(type) { + case *ir.DebugRef: + case *ir.Sigma: + if seen == nil { + seen = map[ir.Value]struct{}{} + } + seen[v] = struct{}{} + if hasUse(ref, seen) { + return true + } + case *ir.Phi: + if seen == nil { + seen = map[ir.Value]struct{}{} + } + seen[v] = struct{}{} + if hasUse(ref, seen) { + return true + } + default: + return true + } + } + return false + } + + ast.Inspect(node, func(node ast.Node) bool { + assign, ok := node.(*ast.AssignStmt) + if !ok { + return true + } + if len(assign.Lhs) > 1 && len(assign.Rhs) == 1 { + // Either a function call with multiple return values, + // or a comma-ok assignment + + val, _ := fn.ValueForExpr(assign.Rhs[0]) + if val == nil { + return true + } + refs := val.Referrers() + if refs == nil { + return true + } + for _, ref := range *refs { + ex, ok := ref.(*ir.Extract) + if !ok { + continue + } + if !hasUse(ex, nil) { + lhs := assign.Lhs[ex.Index] + if ident, ok := lhs.(*ast.Ident); !ok || ok && ident.Name == "_" { + continue + } + report.Report(pass, assign, fmt.Sprintf("this value of %s is never used", lhs)) + } + } + return true + } + for i, lhs := range assign.Lhs { + rhs := assign.Rhs[i] + if ident, ok := lhs.(*ast.Ident); !ok || ok && ident.Name == "_" { + continue + } + val, _ := fn.ValueForExpr(rhs) + if val == nil { + continue + } + + if _, ok := val.(*ir.Const); ok { + // a zero-valued constant, for example in 'foo := []string(nil)' + continue + } + if !hasUse(val, nil) { + report.Report(pass, assign, fmt.Sprintf("this value of %s is never used", lhs)) + } + } + return true + }) + } + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa4008/sa4008.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa4008/sa4008.go new file mode 100644 index 0000000000..704273723f --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa4008/sa4008.go @@ -0,0 +1,114 @@ +package sa4008 + +import ( + "go/ast" + + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/ir" + "honnef.co/go/tools/internal/passes/buildir" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA4008", + Run: run, + Requires: []*analysis.Analyzer{buildir.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `The variable in the loop condition never changes, are you incrementing the wrong variable?`, + Text: `For example: + + for i := 0; i < 10; j++ { ... } + +This may also occur when a loop can only execute once because of unconditional +control flow that terminates the loop. For example, when a loop body contains an +unconditional break, return, or panic: + + func f() { + panic("oops") + } + func g() { + for i := 0; i < 10; i++ { + // f unconditionally calls panic, which means "i" is + // never incremented. + f() + } + }`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAll, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { + cb := func(node ast.Node) bool { + loop, ok := node.(*ast.ForStmt) + if !ok { + return true + } + if loop.Init == nil || loop.Cond == nil || loop.Post == nil { + return true + } + init, ok := loop.Init.(*ast.AssignStmt) + if !ok || len(init.Lhs) != 1 || len(init.Rhs) != 1 { + return true + } + cond, ok := loop.Cond.(*ast.BinaryExpr) + if !ok { + return true + } + x, ok := cond.X.(*ast.Ident) + if !ok { + return true + } + lhs, ok := init.Lhs[0].(*ast.Ident) + if !ok { + return true + } + if pass.TypesInfo.ObjectOf(x) != pass.TypesInfo.ObjectOf(lhs) { + return true + } + if _, ok := loop.Post.(*ast.IncDecStmt); !ok { + return true + } + + v, isAddr := fn.ValueForExpr(cond.X) + if v == nil || isAddr { + return true + } + switch v := v.(type) { + case *ir.Phi: + ops := v.Operands(nil) + if len(ops) != 2 { + return true + } + _, ok := (*ops[0]).(*ir.Const) + if !ok { + return true + } + sigma, ok := (*ops[1]).(*ir.Sigma) + if !ok { + return true + } + if sigma.X != v { + return true + } + case *ir.Load: + return true + } + report.Report(pass, cond, "variable in loop condition never changes") + + return true + } + if source := fn.Source(); source != nil { + ast.Inspect(source, cb) + } + } + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa4009/sa4009.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa4009/sa4009.go new file mode 100644 index 0000000000..c1aa50bd49 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa4009/sa4009.go @@ -0,0 +1,106 @@ +package sa4009 + +import ( + "fmt" + "go/ast" + + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/ir" + "honnef.co/go/tools/go/ir/irutil" + "honnef.co/go/tools/internal/passes/buildir" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA4009", + Run: run, + Requires: []*analysis.Analyzer{buildir.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `A function argument is overwritten before its first use`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { + cb := func(node ast.Node) bool { + var typ *ast.FuncType + var body *ast.BlockStmt + switch fn := node.(type) { + case *ast.FuncDecl: + typ = fn.Type + body = fn.Body + case *ast.FuncLit: + typ = fn.Type + body = fn.Body + } + if body == nil { + return true + } + if len(typ.Params.List) == 0 { + return true + } + for _, field := range typ.Params.List { + for _, arg := range field.Names { + obj := pass.TypesInfo.ObjectOf(arg) + var irobj *ir.Parameter + for _, param := range fn.Params { + if param.Object() == obj { + irobj = param + break + } + } + if irobj == nil { + continue + } + refs := irobj.Referrers() + if refs == nil { + continue + } + if len(irutil.FilterDebug(*refs)) != 0 { + continue + } + + var assignment ast.Node + ast.Inspect(body, func(node ast.Node) bool { + if assignment != nil { + return false + } + assign, ok := node.(*ast.AssignStmt) + if !ok { + return true + } + for _, lhs := range assign.Lhs { + ident, ok := lhs.(*ast.Ident) + if !ok { + continue + } + if pass.TypesInfo.ObjectOf(ident) == obj { + assignment = assign + return false + } + } + return true + }) + if assignment != nil { + report.Report(pass, arg, fmt.Sprintf("argument %s is overwritten before first use", arg), + report.Related(assignment, fmt.Sprintf("assignment to %s", arg))) + } + } + } + return true + } + if source := fn.Source(); source != nil { + ast.Inspect(source, cb) + } + } + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa4010/sa4010.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa4010/sa4010.go new file mode 100644 index 0000000000..771ebbd1ab --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa4010/sa4010.go @@ -0,0 +1,216 @@ +package sa4010 + +import ( + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/ir" + "honnef.co/go/tools/internal/passes/buildir" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA4010", + Run: run, + Requires: []*analysis.Analyzer{buildir.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `The result of \'append\' will never be observed anywhere`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAll, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + isAppend := func(ins ir.Value) bool { + call, ok := ins.(*ir.Call) + if !ok { + return false + } + if call.Call.IsInvoke() { + return false + } + if builtin, ok := call.Call.Value.(*ir.Builtin); !ok || builtin.Name() != "append" { + return false + } + return true + } + + // We have to be careful about aliasing. + // Multiple slices may refer to the same backing array, + // making appends observable even when we don't see the result of append be used anywhere. + // + // We will have to restrict ourselves to slices that have been allocated within the function, + // haven't been sliced, + // and haven't been passed anywhere that could retain them (such as function calls or memory stores). + // + // We check whether an append should be flagged in two steps. + // + // In the first step, we look at the data flow graph, starting in reverse from the argument to append, till we reach the root. + // This graph must only consist of the following instructions: + // + // - phi + // - sigma + // - slice + // - const nil + // - MakeSlice + // - Alloc + // - calls to append + // + // If this step succeeds, we look at all referrers of the values found in the first step, recursively. + // These referrers must either be in the set of values found in the first step, + // be DebugRefs, + // or fulfill the same type requirements as step 1, with the exception of appends, which are forbidden. + // + // If both steps succeed then we know that the backing array hasn't been aliased in an observable manner. + // + // We could relax these restrictions by making use of additional information: + // - if we passed the slice to a function that doesn't retain the slice then we can still flag it + // - if a slice has been sliced but is dead afterwards, we can flag appends to the new slice + + // OPT(dh): We could cache the results of both validate functions. + // However, we only use these functions on values that we otherwise want to flag, which are very few. + // Not caching values hasn't increased the runtimes for the standard library nor k8s. + var validateArgument func(v ir.Value, seen map[ir.Value]struct{}) bool + validateArgument = func(v ir.Value, seen map[ir.Value]struct{}) bool { + if _, ok := seen[v]; ok { + // break cycle + return true + } + seen[v] = struct{}{} + switch v := v.(type) { + case *ir.Phi: + for _, edge := range v.Edges { + if !validateArgument(edge, seen) { + return false + } + } + return true + case *ir.Sigma: + return validateArgument(v.X, seen) + case *ir.Slice: + return validateArgument(v.X, seen) + case *ir.Const: + return true + case *ir.MakeSlice: + return true + case *ir.Alloc: + return true + case *ir.Call: + if isAppend(v) { + return validateArgument(v.Call.Args[0], seen) + } + return false + default: + return false + } + } + + var validateReferrers func(v ir.Value, seen map[ir.Instruction]struct{}) bool + validateReferrers = func(v ir.Value, seen map[ir.Instruction]struct{}) bool { + for _, ref := range *v.Referrers() { + if _, ok := seen[ref]; ok { + continue + } + + seen[ref] = struct{}{} + switch ref.(type) { + case *ir.Phi: + case *ir.Sigma: + case *ir.Slice: + case *ir.Const: + case *ir.MakeSlice: + case *ir.Alloc: + case *ir.DebugRef: + default: + return false + } + + if ref, ok := ref.(ir.Value); ok { + if !validateReferrers(ref, seen) { + return false + } + } + } + return true + } + + for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { + for _, block := range fn.Blocks { + for _, ins := range block.Instrs { + val, ok := ins.(ir.Value) + if !ok || !isAppend(val) { + continue + } + + isUsed := false + visited := map[ir.Instruction]bool{} + var walkRefs func(refs []ir.Instruction) + walkRefs = func(refs []ir.Instruction) { + loop: + for _, ref := range refs { + if visited[ref] { + continue + } + visited[ref] = true + if _, ok := ref.(*ir.DebugRef); ok { + continue + } + switch ref := ref.(type) { + case *ir.Phi: + walkRefs(*ref.Referrers()) + case *ir.Sigma: + walkRefs(*ref.Referrers()) + case ir.Value: + if !isAppend(ref) { + isUsed = true + } else { + walkRefs(*ref.Referrers()) + } + case ir.Instruction: + isUsed = true + break loop + } + } + } + + refs := val.Referrers() + if refs == nil { + continue + } + walkRefs(*refs) + + if isUsed { + continue + } + + seen := map[ir.Value]struct{}{} + if !validateArgument(ins.(*ir.Call).Call.Args[0], seen) { + continue + } + + seen2 := map[ir.Instruction]struct{}{} + for k := range seen { + // the only values we allow are also instructions, so this type assertion cannot fail + seen2[k.(ir.Instruction)] = struct{}{} + } + seen2[ins] = struct{}{} + failed := false + for v := range seen { + if !validateReferrers(v, seen2) { + failed = true + break + } + } + if !failed { + report.Report(pass, ins, "this result of append is never used, except maybe in other appends") + } + } + } + } + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa4011/sa4011.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa4011/sa4011.go new file mode 100644 index 0000000000..f9197bddf3 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa4011/sa4011.go @@ -0,0 +1,88 @@ +package sa4011 + +import ( + "go/ast" + "go/token" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA4011", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Break statement with no effect. Did you mean to break out of an outer loop?`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + var body *ast.BlockStmt + switch node := node.(type) { + case *ast.ForStmt: + body = node.Body + case *ast.RangeStmt: + body = node.Body + default: + lint.ExhaustiveTypeSwitch(node) + } + for _, stmt := range body.List { + var blocks [][]ast.Stmt + switch stmt := stmt.(type) { + case *ast.SwitchStmt: + for _, c := range stmt.Body.List { + blocks = append(blocks, c.(*ast.CaseClause).Body) + } + case *ast.SelectStmt: + for _, c := range stmt.Body.List { + blocks = append(blocks, c.(*ast.CommClause).Body) + } + default: + continue + } + + for _, body := range blocks { + if len(body) == 0 { + continue + } + lasts := []ast.Stmt{body[len(body)-1]} + // TODO(dh): unfold all levels of nested block + // statements, not just a single level if statement + if ifs, ok := lasts[0].(*ast.IfStmt); ok { + if len(ifs.Body.List) == 0 { + continue + } + lasts[0] = ifs.Body.List[len(ifs.Body.List)-1] + + if block, ok := ifs.Else.(*ast.BlockStmt); ok { + if len(block.List) != 0 { + lasts = append(lasts, block.List[len(block.List)-1]) + } + } + } + for _, last := range lasts { + branch, ok := last.(*ast.BranchStmt) + if !ok || branch.Tok != token.BREAK || branch.Label != nil { + continue + } + report.Report(pass, branch, "ineffective break statement. Did you mean to break out of the outer loop?") + } + } + } + } + code.Preorder(pass, fn, (*ast.ForStmt)(nil), (*ast.RangeStmt)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa4012/sa4012.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa4012/sa4012.go new file mode 100644 index 0000000000..d2e42a3e92 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa4012/sa4012.go @@ -0,0 +1,51 @@ +package sa4012 + +import ( + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/ir" + "honnef.co/go/tools/go/ir/irutil" + "honnef.co/go/tools/internal/passes/buildir" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA4012", + Run: run, + Requires: []*analysis.Analyzer{buildir.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Comparing a value against NaN even though no value is equal to NaN`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + isNaN := func(v ir.Value) bool { + call, ok := v.(*ir.Call) + if !ok { + return false + } + return irutil.IsCallTo(call.Common(), "math.NaN") + } + for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { + for _, block := range fn.Blocks { + for _, ins := range block.Instrs { + ins, ok := ins.(*ir.BinOp) + if !ok { + continue + } + if isNaN(irutil.Flatten(ins.X)) || isNaN(irutil.Flatten(ins.Y)) { + report.Report(pass, ins, "no value is equal to NaN, not even NaN itself") + } + } + } + } + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa4013/sa4013.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa4013/sa4013.go new file mode 100644 index 0000000000..ca451c6221 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa4013/sa4013.go @@ -0,0 +1,44 @@ +package sa4013 + +import ( + "go/ast" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/edit" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA4013", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Negating a boolean twice (\'!!b\') is the same as writing \'b\'. This is either redundant, or a typo.`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var checkDoubleNegationQ = pattern.MustParse(`(UnaryExpr "!" single@(UnaryExpr "!" x))`) + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + if m, ok := code.Match(pass, checkDoubleNegationQ, node); ok { + report.Report(pass, node, "negating a boolean twice has no effect; is this a typo?", report.Fixes( + edit.Fix("turn into single negation", edit.ReplaceWithNode(pass.Fset, node, m.State["single"].(ast.Node))), + edit.Fix("remove double negation", edit.ReplaceWithNode(pass.Fset, node, m.State["x"].(ast.Node))))) + } + } + code.Preorder(pass, fn, (*ast.UnaryExpr)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa4014/sa4014.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa4014/sa4014.go new file mode 100644 index 0000000000..642c80ca67 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa4014/sa4014.go @@ -0,0 +1,78 @@ +package sa4014 + +import ( + "go/ast" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA4014", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `An if/else if chain has repeated conditions and no side-effects; if the condition didn't match the first time, it won't match the second time, either`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAll, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + seen := map[ast.Node]bool{} + + var collectConds func(ifstmt *ast.IfStmt, conds []ast.Expr) ([]ast.Expr, bool) + collectConds = func(ifstmt *ast.IfStmt, conds []ast.Expr) ([]ast.Expr, bool) { + seen[ifstmt] = true + // Bail if any if-statement has an Init statement or side effects in its condition + if ifstmt.Init != nil { + return nil, false + } + if code.MayHaveSideEffects(pass, ifstmt.Cond, nil) { + return nil, false + } + + conds = append(conds, ifstmt.Cond) + if elsestmt, ok := ifstmt.Else.(*ast.IfStmt); ok { + return collectConds(elsestmt, conds) + } + return conds, true + } + fn := func(node ast.Node) { + ifstmt := node.(*ast.IfStmt) + if seen[ifstmt] { + // this if-statement is part of an if/else-if chain that we've already processed + return + } + if ifstmt.Else == nil { + // there can be at most one condition + return + } + conds, ok := collectConds(ifstmt, nil) + if !ok { + return + } + if len(conds) < 2 { + return + } + counts := map[string]int{} + for _, cond := range conds { + s := report.Render(pass, cond) + counts[s]++ + if counts[s] == 2 { + report.Report(pass, cond, "this condition occurs multiple times in this if/else if chain") + } + } + } + code.Preorder(pass, fn, (*ast.IfStmt)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa4015/sa4015.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa4015/sa4015.go new file mode 100644 index 0000000000..d94a89df65 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa4015/sa4015.go @@ -0,0 +1,56 @@ +package sa4015 + +import ( + "fmt" + "go/types" + + "honnef.co/go/tools/analysis/callcheck" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/go/ir" + "honnef.co/go/tools/go/ir/irutil" + "honnef.co/go/tools/go/types/typeutil" + "honnef.co/go/tools/internal/passes/buildir" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA4015", + Requires: []*analysis.Analyzer{buildir.Analyzer}, + Run: callcheck.Analyzer(checkMathIntRules), + }, + Doc: &lint.RawDocumentation{ + Title: `Calling functions like \'math.Ceil\' on floats converted from integers doesn't do anything useful`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAll, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var checkMathIntRules = map[string]callcheck.Check{ + "math.Ceil": pointlessIntMath, + "math.Floor": pointlessIntMath, + "math.IsNaN": pointlessIntMath, + "math.Trunc": pointlessIntMath, + "math.IsInf": pointlessIntMath, +} + +func pointlessIntMath(call *callcheck.Call) { + if ConvertedFromInt(call.Args[0].Value) { + call.Invalid(fmt.Sprintf("calling %s on a converted integer is pointless", irutil.CallName(call.Instr.Common()))) + } +} + +func ConvertedFromInt(v callcheck.Value) bool { + conv, ok := v.Value.(*ir.Convert) + if !ok { + return false + } + return typeutil.NewTypeSet(conv.X.Type()).All(func(t *types.Term) bool { + b, ok := t.Type().Underlying().(*types.Basic) + return ok && b.Info()&types.IsInteger != 0 + }) +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa4016/sa4016.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa4016/sa4016.go new file mode 100644 index 0000000000..cc644370ca --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa4016/sa4016.go @@ -0,0 +1,116 @@ +package sa4016 + +import ( + "fmt" + "go/ast" + "go/constant" + "go/token" + "go/types" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/ast/astutil" + "honnef.co/go/tools/go/types/typeutil" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA4016", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Certain bitwise operations, such as \'x ^ 0\', do not do anything useful`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, // MergeIfAny if we only flag literals, not named constants + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + binop := node.(*ast.BinaryExpr) + if !typeutil.All(pass.TypesInfo.TypeOf(binop), func(term *types.Term) bool { + b, ok := term.Type().Underlying().(*types.Basic) + if !ok { + return false + } + return (b.Info() & types.IsInteger) != 0 + }) { + return + } + switch binop.Op { + case token.AND, token.OR, token.XOR: + default: + // we do not flag shifts because too often, x<<0 is part + // of a pattern, x<<0, x<<8, x<<16, ... + return + } + if y, ok := binop.Y.(*ast.Ident); ok { + obj, ok := pass.TypesInfo.ObjectOf(y).(*types.Const) + if !ok { + return + } + if obj.Pkg() != pass.Pkg { + // identifier was dot-imported + return + } + if v, _ := constant.Int64Val(obj.Val()); v != 0 { + return + } + path, _ := astutil.PathEnclosingInterval(code.File(pass, obj), obj.Pos(), obj.Pos()) + if len(path) < 2 { + return + } + spec, ok := path[1].(*ast.ValueSpec) + if !ok { + return + } + if len(spec.Names) != 1 || len(spec.Values) != 1 { + // TODO(dh): we could support this + return + } + ident, ok := spec.Values[0].(*ast.Ident) + if !ok { + return + } + if !isIota(pass.TypesInfo.ObjectOf(ident)) { + return + } + switch binop.Op { + case token.AND: + report.Report(pass, node, + fmt.Sprintf("%s always equals 0; %s is defined as iota and has value 0, maybe %s is meant to be 1 << iota?", report.Render(pass, binop), report.Render(pass, binop.Y), report.Render(pass, binop.Y))) + case token.OR, token.XOR: + report.Report(pass, node, + fmt.Sprintf("%s always equals %s; %s is defined as iota and has value 0, maybe %s is meant to be 1 << iota?", report.Render(pass, binop), report.Render(pass, binop.X), report.Render(pass, binop.Y), report.Render(pass, binop.Y))) + } + } else if code.IsIntegerLiteral(pass, binop.Y, constant.MakeInt64(0)) { + switch binop.Op { + case token.AND: + report.Report(pass, node, fmt.Sprintf("%s always equals 0", report.Render(pass, binop))) + case token.OR, token.XOR: + report.Report(pass, node, fmt.Sprintf("%s always equals %s", report.Render(pass, binop), report.Render(pass, binop.X))) + } + } + } + code.Preorder(pass, fn, (*ast.BinaryExpr)(nil)) + return nil, nil +} + +func isIota(obj types.Object) bool { + if obj.Name() != "iota" { + return false + } + c, ok := obj.(*types.Const) + if !ok { + return false + } + return c.Pkg() == nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa4017/sa4017.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa4017/sa4017.go new file mode 100644 index 0000000000..f0d10a4b9b --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa4017/sa4017.go @@ -0,0 +1,87 @@ +package sa4017 + +import ( + "fmt" + "go/types" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/facts/purity" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/ir" + "honnef.co/go/tools/go/ir/irutil" + "honnef.co/go/tools/go/types/typeutil" + "honnef.co/go/tools/internal/passes/buildir" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA4017", + Run: run, + Requires: []*analysis.Analyzer{buildir.Analyzer, purity.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Discarding the return values of a function without side effects, making the call pointless`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAll, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + pure := pass.ResultOf[purity.Analyzer].(purity.Result) + +fnLoop: + for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { + if code.IsInTest(pass, fn) { + params := fn.Signature.Params() + for i := 0; i < params.Len(); i++ { + param := params.At(i) + if typeutil.IsPointerToTypeWithName(param.Type(), "testing.B") { + // Ignore discarded pure functions in code related + // to benchmarks. Instead of matching BenchmarkFoo + // functions, we match any function accepting a + // *testing.B. Benchmarks sometimes call generic + // functions for doing the actual work, and + // checking for the parameter is a lot easier and + // faster than analyzing call trees. + continue fnLoop + } + } + } + + for _, b := range fn.Blocks { + for _, ins := range b.Instrs { + ins, ok := ins.(*ir.Call) + if !ok { + continue + } + refs := ins.Referrers() + if refs == nil || len(irutil.FilterDebug(*refs)) > 0 { + continue + } + + callee := ins.Common().StaticCallee() + if callee == nil { + continue + } + if callee.Object() == nil { + // TODO(dh): support anonymous functions + continue + } + if _, ok := pure[callee.Object().(*types.Func)]; ok { + if pass.Pkg.Path() == "fmt_test" && callee.Object().(*types.Func).FullName() == "fmt.Sprintf" { + // special case for benchmarks in the fmt package + continue + } + report.Report(pass, ins, fmt.Sprintf("%s doesn't have side effects and its return value is ignored", callee.Object().Name())) + } + } + } + } + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa4018/sa4018.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa4018/sa4018.go new file mode 100644 index 0000000000..f04ed33efc --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa4018/sa4018.go @@ -0,0 +1,61 @@ +package sa4018 + +import ( + "fmt" + "go/ast" + "go/token" + "reflect" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/facts/purity" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA4018", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer, purity.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Self-assignment of variables`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + pure := pass.ResultOf[purity.Analyzer].(purity.Result) + + fn := func(node ast.Node) { + assign := node.(*ast.AssignStmt) + if assign.Tok != token.ASSIGN || len(assign.Lhs) != len(assign.Rhs) { + return + } + for i, lhs := range assign.Lhs { + rhs := assign.Rhs[i] + if reflect.TypeOf(lhs) != reflect.TypeOf(rhs) { + continue + } + if code.MayHaveSideEffects(pass, lhs, pure) || code.MayHaveSideEffects(pass, rhs, pure) { + continue + } + + rlh := report.Render(pass, lhs) + rrh := report.Render(pass, rhs) + if rlh == rrh { + report.Report(pass, assign, fmt.Sprintf("self-assignment of %s to %s", rrh, rlh), report.FilterGenerated()) + } + } + } + code.Preorder(pass, fn, (*ast.AssignStmt)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa4019/sa4019.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa4019/sa4019.go new file mode 100644 index 0000000000..dca7d23f1a --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa4019/sa4019.go @@ -0,0 +1,82 @@ +package sa4019 + +import ( + "fmt" + "go/ast" + "sort" + "strings" + + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/ast/astutil" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA4019", + Run: run, + Requires: []*analysis.Analyzer{generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Multiple, identical build constraints in the same file`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func buildTagsIdentical(s1, s2 []string) bool { + if len(s1) != len(s2) { + return false + } + s1s := make([]string, len(s1)) + copy(s1s, s1) + sort.Strings(s1s) + s2s := make([]string, len(s2)) + copy(s2s, s2) + sort.Strings(s2s) + for i, s := range s1s { + if s != s2s[i] { + return false + } + } + return true +} + +func run(pass *analysis.Pass) (interface{}, error) { + for _, f := range pass.Files { + constraints := buildTags(f) + for i, constraint1 := range constraints { + for j, constraint2 := range constraints { + if i >= j { + continue + } + if buildTagsIdentical(constraint1, constraint2) { + msg := fmt.Sprintf("identical build constraints %q and %q", + strings.Join(constraint1, " "), + strings.Join(constraint2, " ")) + report.Report(pass, f, msg, report.FilterGenerated(), report.ShortRange()) + } + } + } + } + return nil, nil +} + +func buildTags(f *ast.File) [][]string { + var out [][]string + for _, line := range strings.Split(astutil.Preamble(f), "\n") { + if !strings.HasPrefix(line, "+build ") { + continue + } + line = strings.TrimSpace(strings.TrimPrefix(line, "+build ")) + fields := strings.Fields(line) + out = append(out, fields) + } + return out +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa4020/sa4020.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa4020/sa4020.go new file mode 100644 index 0000000000..8994cc5d77 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa4020/sa4020.go @@ -0,0 +1,172 @@ +package sa4020 + +import ( + "fmt" + "go/ast" + "go/types" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + + "golang.org/x/exp/typeparams" + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA4020", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Unreachable case clause in a type switch`, + Text: `In a type switch like the following + + type T struct{} + func (T) Read(b []byte) (int, error) { return 0, nil } + + var v interface{} = T{} + + switch v.(type) { + case io.Reader: + // ... + case T: + // unreachable + } + +the second case clause can never be reached because \'T\' implements +\'io.Reader\' and case clauses are evaluated in source order. + +Another example: + + type T struct{} + func (T) Read(b []byte) (int, error) { return 0, nil } + func (T) Close() error { return nil } + + var v interface{} = T{} + + switch v.(type) { + case io.Reader: + // ... + case io.ReadCloser: + // unreachable + } + +Even though \'T\' has a \'Close\' method and thus implements \'io.ReadCloser\', +\'io.Reader\' will always match first. The method set of \'io.Reader\' is a +subset of \'io.ReadCloser\'. Thus it is impossible to match the second +case without matching the first case. + + +Structurally equivalent interfaces + +A special case of the previous example are structurally identical +interfaces. Given these declarations + + type T error + type V error + + func doSomething() error { + err, ok := doAnotherThing() + if ok { + return T(err) + } + + return U(err) + } + +the following type switch will have an unreachable case clause: + + switch doSomething().(type) { + case T: + // ... + case V: + // unreachable + } + +\'T\' will always match before V because they are structurally equivalent +and therefore \'doSomething()\''s return value implements both.`, + Since: "2019.2", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAll, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + // Check if T subsumes V in a type switch. T subsumes V if T is an interface and T's method set is a subset of V's method set. + subsumes := func(T, V types.Type) bool { + if typeparams.IsTypeParam(T) { + return false + } + tIface, ok := T.Underlying().(*types.Interface) + if !ok { + return false + } + + return types.Implements(V, tIface) + } + + subsumesAny := func(Ts, Vs []types.Type) (types.Type, types.Type, bool) { + for _, T := range Ts { + for _, V := range Vs { + if subsumes(T, V) { + return T, V, true + } + } + } + + return nil, nil, false + } + + fn := func(node ast.Node) { + tsStmt := node.(*ast.TypeSwitchStmt) + + type ccAndTypes struct { + cc *ast.CaseClause + types []types.Type + } + + // All asserted types in the order of case clauses. + ccs := make([]ccAndTypes, 0, len(tsStmt.Body.List)) + for _, stmt := range tsStmt.Body.List { + cc, _ := stmt.(*ast.CaseClause) + + // Exclude the 'default' case. + if len(cc.List) == 0 { + continue + } + + Ts := make([]types.Type, 0, len(cc.List)) + for _, expr := range cc.List { + // Exclude the 'nil' value from any 'case' statement (it is always reachable). + if typ := pass.TypesInfo.TypeOf(expr); typ != types.Typ[types.UntypedNil] { + Ts = append(Ts, typ) + } + } + + ccs = append(ccs, ccAndTypes{cc: cc, types: Ts}) + } + + if len(ccs) <= 1 { + // Zero or one case clauses, nothing to check. + return + } + + // Check if case clauses following cc have types that are subsumed by cc. + for i, cc := range ccs[:len(ccs)-1] { + for _, next := range ccs[i+1:] { + if T, V, yes := subsumesAny(cc.types, next.types); yes { + report.Report(pass, next.cc, fmt.Sprintf("unreachable case clause: %s will always match before %s", T.String(), V.String()), + report.ShortRange()) + } + } + } + } + + code.Preorder(pass, fn, (*ast.TypeSwitchStmt)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa4021/sa4021.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa4021/sa4021.go new file mode 100644 index 0000000000..76d9235240 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa4021/sa4021.go @@ -0,0 +1,44 @@ +package sa4021 + +import ( + "go/ast" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA4021", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `\"x = append(y)\" is equivalent to \"x = y\"`, + Since: "2019.2", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var checkSingleArgAppendQ = pattern.MustParse(`(CallExpr (Builtin "append") [_])`) + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + _, ok := code.Match(pass, checkSingleArgAppendQ, node) + if !ok { + return + } + report.Report(pass, node, "x = append(y) is equivalent to x = y", report.FilterGenerated()) + } + code.Preorder(pass, fn, (*ast.CallExpr)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa4022/sa4022.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa4022/sa4022.go new file mode 100644 index 0000000000..c6575442ca --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa4022/sa4022.go @@ -0,0 +1,48 @@ +package sa4022 + +import ( + "go/ast" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA4022", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Comparing the address of a variable against nil`, + Text: `Code such as \"if &x == nil\" is meaningless, because taking the address of a variable always yields a non-nil pointer.`, + Since: "2020.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var CheckAddressIsNilQ = pattern.MustParse( + `(BinaryExpr + (UnaryExpr "&" _) + (Or "==" "!=") + (Builtin "nil"))`) + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + _, ok := code.Match(pass, CheckAddressIsNilQ, node) + if !ok { + return + } + report.Report(pass, node, "the address of a variable cannot be nil") + } + code.Preorder(pass, fn, (*ast.BinaryExpr)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa4023/sa4023.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa4023/sa4023.go new file mode 100644 index 0000000000..991d4f8195 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa4023/sa4023.go @@ -0,0 +1,205 @@ +package sa4023 + +import ( + "fmt" + "go/token" + "go/types" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/facts/nilness" + "honnef.co/go/tools/analysis/facts/typedness" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/ir" + "honnef.co/go/tools/go/ir/irutil" + "honnef.co/go/tools/go/types/typeutil" + "honnef.co/go/tools/internal/passes/buildir" + + "golang.org/x/exp/typeparams" + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA4023", + Run: run, + Requires: []*analysis.Analyzer{buildir.Analyzer, typedness.Analysis, nilness.Analysis}, + }, + Doc: &lint.RawDocumentation{ + Title: `Impossible comparison of interface value with untyped nil`, + Text: `Under the covers, interfaces are implemented as two elements, a +type T and a value V. V is a concrete value such as an int, +struct or pointer, never an interface itself, and has type T. For +instance, if we store the int value 3 in an interface, the +resulting interface value has, schematically, (T=int, V=3). The +value V is also known as the interface's dynamic value, since a +given interface variable might hold different values V (and +corresponding types T) during the execution of the program. + +An interface value is nil only if the V and T are both +unset, (T=nil, V is not set), In particular, a nil interface will +always hold a nil type. If we store a nil pointer of type *int +inside an interface value, the inner type will be *int regardless +of the value of the pointer: (T=*int, V=nil). Such an interface +value will therefore be non-nil even when the pointer value V +inside is nil. + +This situation can be confusing, and arises when a nil value is +stored inside an interface value such as an error return: + + func returnsError() error { + var p *MyError = nil + if bad() { + p = ErrBad + } + return p // Will always return a non-nil error. + } + +If all goes well, the function returns a nil p, so the return +value is an error interface value holding (T=*MyError, V=nil). +This means that if the caller compares the returned error to nil, +it will always look as if there was an error even if nothing bad +happened. To return a proper nil error to the caller, the +function must return an explicit nil: + + func returnsError() error { + if bad() { + return ErrBad + } + return nil + } + +It's a good idea for functions that return errors always to use +the error type in their signature (as we did above) rather than a +concrete type such as \'*MyError\', to help guarantee the error is +created correctly. As an example, \'os.Open\' returns an error even +though, if not nil, it's always of concrete type *os.PathError. + +Similar situations to those described here can arise whenever +interfaces are used. Just keep in mind that if any concrete value +has been stored in the interface, the interface will not be nil. +For more information, see The Laws of +Reflection (https://golang.org/doc/articles/laws_of_reflection.html). + +This text has been copied from +https://golang.org/doc/faq#nil_error, licensed under the Creative +Commons Attribution 3.0 License.`, + Since: "2020.2", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, // TODO should this be MergeIfAll? + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + // The comparison 'fn() == nil' can never be true if fn() returns + // an interface value and only returns typed nils. This is usually + // a mistake in the function itself, but all we can say for + // certain is that the comparison is pointless. + // + // Flag results if no untyped nils are being returned, but either + // known typed nils, or typed unknown nilness are being returned. + + irpkg := pass.ResultOf[buildir.Analyzer].(*buildir.IR) + typedness := pass.ResultOf[typedness.Analysis].(*typedness.Result) + nilness := pass.ResultOf[nilness.Analysis].(*nilness.Result) + for _, fn := range irpkg.SrcFuncs { + for _, b := range fn.Blocks { + for _, instr := range b.Instrs { + binop, ok := instr.(*ir.BinOp) + if !ok || !(binop.Op == token.EQL || binop.Op == token.NEQ) { + continue + } + if _, ok := binop.X.Type().Underlying().(*types.Interface); !ok || typeparams.IsTypeParam(binop.X.Type()) { + // TODO support swapped X and Y + continue + } + + k, ok := binop.Y.(*ir.Const) + if !ok || !k.IsNil() { + // if binop.X is an interface, then binop.Y can + // only be a Const if its untyped. A typed nil + // constant would first be passed to + // MakeInterface. + continue + } + + var idx int + var obj *types.Func + switch x := irutil.Flatten(binop.X).(type) { + case *ir.Call: + callee := x.Call.StaticCallee() + if callee == nil { + continue + } + obj, _ = callee.Object().(*types.Func) + idx = 0 + case *ir.Extract: + call, ok := irutil.Flatten(x.Tuple).(*ir.Call) + if !ok { + continue + } + callee := call.Call.StaticCallee() + if callee == nil { + continue + } + obj, _ = callee.Object().(*types.Func) + idx = x.Index + case *ir.MakeInterface: + var qualifier string + switch binop.Op { + case token.EQL: + qualifier = "never" + case token.NEQ: + qualifier = "always" + default: + panic("unreachable") + } + + terms, err := typeparams.NormalTerms(x.X.Type()) + if len(terms) == 0 || err != nil { + // Type is a type parameter with no type terms (or we couldn't determine the terms). Such a type + // _can_ be nil when put in an interface value. + continue + } + + if report.HasRange(x.X) { + report.Report(pass, binop, fmt.Sprintf("this comparison is %s true", qualifier), + report.Related(x.X, "the lhs of the comparison gets its value from here and has a concrete type")) + } else { + // we can't generate related information for this, so make the diagnostic itself slightly more useful + report.Report(pass, binop, fmt.Sprintf("this comparison is %s true; the lhs of the comparison has been assigned a concretely typed value", qualifier)) + } + continue + } + if obj == nil { + continue + } + + isNil, onlyGlobal := nilness.MayReturnNil(obj, idx) + if typedness.MustReturnTyped(obj, idx) && isNil && !onlyGlobal && !code.IsInTest(pass, binop) { + // Don't flag these comparisons in tests. Tests + // may be explicitly enforcing the invariant that + // a value isn't nil. + + var qualifier string + switch binop.Op { + case token.EQL: + qualifier = "never" + case token.NEQ: + qualifier = "always" + default: + panic("unreachable") + } + report.Report(pass, binop, fmt.Sprintf("this comparison is %s true", qualifier), + // TODO support swapped X and Y + report.Related(binop.X, fmt.Sprintf("the lhs of the comparison is the %s return value of this function call", report.Ordinal(idx+1))), + report.Related(obj, fmt.Sprintf("%s never returns a nil interface value", typeutil.FuncName(obj)))) + } + } + } + } + + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa4024/sa4024.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa4024/sa4024.go new file mode 100644 index 0000000000..fad0742bdd --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa4024/sa4024.go @@ -0,0 +1,66 @@ +package sa4024 + +import ( + "fmt" + "go/ast" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA4024", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Checking for impossible return value from a builtin function`, + Text: `Return values of the \'len\' and \'cap\' builtins cannot be negative. + +See https://golang.org/pkg/builtin/#len and https://golang.org/pkg/builtin/#cap. + +Example: + + if len(slice) < 0 { + fmt.Println("unreachable code") + }`, + Since: "2021.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var builtinLessThanZeroQ = pattern.MustParse(` + (Or + (BinaryExpr + (IntegerLiteral "0") + ">" + (CallExpr builtin@(Builtin (Or "len" "cap")) _)) + (BinaryExpr + (CallExpr builtin@(Builtin (Or "len" "cap")) _) + "<" + (IntegerLiteral "0"))) +`) + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + matcher, ok := code.Match(pass, builtinLessThanZeroQ, node) + if !ok { + return + } + + builtin := matcher.State["builtin"].(*ast.Ident) + report.Report(pass, node, fmt.Sprintf("builtin function %s does not return negative values", builtin.Name)) + } + code.Preorder(pass, fn, (*ast.BinaryExpr)(nil)) + + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa4025/sa4025.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa4025/sa4025.go new file mode 100644 index 0000000000..2a2262b2c4 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa4025/sa4025.go @@ -0,0 +1,77 @@ +package sa4025 + +import ( + "fmt" + "go/ast" + "go/constant" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA4025", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: "Integer division of literals that results in zero", + Text: `When dividing two integer constants, the result will +also be an integer. Thus, a division such as \'2 / 3\' results in \'0\'. +This is true for all of the following examples: + + _ = 2 / 3 + const _ = 2 / 3 + const _ float64 = 2 / 3 + _ = float64(2 / 3) + +Staticcheck will flag such divisions if both sides of the division are +integer literals, as it is highly unlikely that the division was +intended to truncate to zero. Staticcheck will not flag integer +division involving named constants, to avoid noisy positives. +`, + Since: "2021.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var integerDivisionQ = pattern.MustParse(`(BinaryExpr (IntegerLiteral _) "/" (IntegerLiteral _))`) + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + _, ok := code.Match(pass, integerDivisionQ, node) + if !ok { + return + } + + val := constant.ToInt(pass.TypesInfo.Types[node.(ast.Expr)].Value) + if v, ok := constant.Uint64Val(val); ok && v == 0 { + report.Report(pass, node, fmt.Sprintf("the integer division '%s' results in zero", report.Render(pass, node))) + } + + // TODO: we could offer a suggested fix here, but I am not + // sure what it should be. There are many options to choose + // from. + + // Note: we experimented with flagging divisions that truncate + // (e.g. 4 / 3), but it ran into false positives in Go's + // 'time' package, which does this, deliberately: + // + // unixToInternal int64 = (1969*365 + 1969/4 - 1969/100 + 1969/400) * secondsPerDay + // + // The check also found a real bug in other code, but I don't + // think we can outright ban this kind of division. + } + code.Preorder(pass, fn, (*ast.BinaryExpr)(nil)) + + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa4026/sa4026.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa4026/sa4026.go new file mode 100644 index 0000000000..9fde1329b0 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa4026/sa4026.go @@ -0,0 +1,87 @@ +package sa4026 + +import ( + "fmt" + "go/ast" + "go/types" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/edit" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA4026", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: "Go constants cannot express negative zero", + Text: `In IEEE 754 floating point math, zero has a sign and can be positive +or negative. This can be useful in certain numerical code. + +Go constants, however, cannot express negative zero. This means that +the literals \'-0.0\' and \'0.0\' have the same ideal value (zero) and +will both represent positive zero at runtime. + +To explicitly and reliably create a negative zero, you can use the +\'math.Copysign\' function: \'math.Copysign(0, -1)\'.`, + Since: "2021.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var negativeZeroFloatQ = pattern.MustParse(` + (Or + (UnaryExpr + "-" + (BasicLit "FLOAT" "0.0")) + + (UnaryExpr + "-" + (CallExpr conv@(Object (Or "float32" "float64")) lit@(Or (BasicLit "INT" "0") (BasicLit "FLOAT" "0.0")))) + + (CallExpr + conv@(Object (Or "float32" "float64")) + (UnaryExpr "-" lit@(BasicLit "INT" "0"))))`) + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + m, ok := code.Match(pass, negativeZeroFloatQ, node) + if !ok { + return + } + + if conv, ok := m.State["conv"].(*types.TypeName); ok { + var replacement string + // TODO(dh): how does this handle type aliases? + if conv.Name() == "float32" { + replacement = `float32(math.Copysign(0, -1))` + } else { + replacement = `math.Copysign(0, -1)` + } + report.Report(pass, node, + fmt.Sprintf("in Go, the floating-point expression '%s' is the same as '%s(%s)', it does not produce a negative zero", + report.Render(pass, node), + conv.Name(), + report.Render(pass, m.State["lit"])), + report.Fixes(edit.Fix("use math.Copysign to create negative zero", edit.ReplaceWithString(node, replacement)))) + } else { + const replacement = `math.Copysign(0, -1)` + report.Report(pass, node, + "in Go, the floating-point literal '-0.0' is the same as '0.0', it does not produce a negative zero", + report.Fixes(edit.Fix("use math.Copysign to create negative zero", edit.ReplaceWithString(node, replacement)))) + } + } + code.Preorder(pass, fn, (*ast.UnaryExpr)(nil), (*ast.CallExpr)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa4027/sa4027.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa4027/sa4027.go new file mode 100644 index 0000000000..99092fb36c --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa4027/sa4027.go @@ -0,0 +1,64 @@ +package sa4027 + +import ( + "go/ast" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA4027", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `\'(*net/url.URL).Query\' returns a copy, modifying it doesn't change the URL`, + Text: `\'(*net/url.URL).Query\' parses the current value of \'net/url.URL.RawQuery\' +and returns it as a map of type \'net/url.Values\'. Subsequent changes to +this map will not affect the URL unless the map gets encoded and +assigned to the URL's \'RawQuery\'. + +As a consequence, the following code pattern is an expensive no-op: +\'u.Query().Add(key, value)\'.`, + Since: "2021.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var ineffectiveURLQueryAddQ = pattern.MustParse(`(CallExpr (SelectorExpr (CallExpr (SelectorExpr recv (Ident "Query")) []) (Ident meth)) _)`) + +func run(pass *analysis.Pass) (interface{}, error) { + // TODO(dh): We could make this check more complex and detect + // pointless modifications of net/url.Values in general, but that + // requires us to get the state machine correct, else we'll cause + // false positives. + + fn := func(node ast.Node) { + m, ok := code.Match(pass, ineffectiveURLQueryAddQ, node) + if !ok { + return + } + if !code.IsOfPointerToTypeWithName(pass, m.State["recv"].(ast.Expr), "net/url.URL") { + return + } + switch m.State["meth"].(string) { + case "Add", "Del", "Set": + default: + return + } + report.Report(pass, node, "(*net/url.URL).Query returns a copy, modifying it doesn't change the URL") + } + code.Preorder(pass, fn, (*ast.CallExpr)(nil)) + + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa4028/sa4028.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa4028/sa4028.go new file mode 100644 index 0000000000..afcdcdda5a --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa4028/sa4028.go @@ -0,0 +1,43 @@ +package sa4028 + +import ( + "go/ast" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA4028", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `\'x % 1\' is always zero`, + Since: "2022.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, // MergeIfAny if we only flag literals, not named constants + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var moduloOneQ = pattern.MustParse(`(BinaryExpr _ "%" (IntegerLiteral "1"))`) + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + _, ok := code.Match(pass, moduloOneQ, node) + if !ok { + return + } + report.Report(pass, node, "x % 1 is always zero") + } + code.Preorder(pass, fn, (*ast.BinaryExpr)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa4029/sa4029.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa4029/sa4029.go new file mode 100644 index 0000000000..a2d830ee76 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa4029/sa4029.go @@ -0,0 +1,87 @@ +package sa4029 + +import ( + "fmt" + "go/ast" + "go/types" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/edit" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA4029", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: "Ineffective attempt at sorting slice", + Text: ` +\'sort.Float64Slice\', \'sort.IntSlice\', and \'sort.StringSlice\' are +types, not functions. Doing \'x = sort.StringSlice(x)\' does nothing, +especially not sort any values. The correct usage is +\'sort.Sort(sort.StringSlice(x))\' or \'sort.StringSlice(x).Sort()\', +but there are more convenient helpers, namely \'sort.Float64s\', +\'sort.Ints\', and \'sort.Strings\'. +`, + Since: "2022.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var ineffectiveSortQ = pattern.MustParse(`(AssignStmt target@(Ident _) "=" (CallExpr typ@(Symbol (Or "sort.Float64Slice" "sort.IntSlice" "sort.StringSlice")) [target]))`) + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + m, ok := code.Match(pass, ineffectiveSortQ, node) + if !ok { + return + } + + _, ok = types.Unalias(pass.TypesInfo.TypeOf(m.State["target"].(ast.Expr))).(*types.Slice) + if !ok { + // Avoid flagging 'x = sort.StringSlice(x)' where TypeOf(x) == sort.StringSlice + return + } + + var alternative string + typeName := types.TypeString(types.Unalias(m.State["typ"].(*types.TypeName).Type()), nil) + switch typeName { + case "sort.Float64Slice": + alternative = "Float64s" + case "sort.IntSlice": + alternative = "Ints" + case "sort.StringSlice": + alternative = "Strings" + default: + panic(fmt.Sprintf("unreachable: %q", typeName)) + } + + r := &ast.CallExpr{ + Fun: &ast.SelectorExpr{ + X: &ast.Ident{Name: "sort"}, + Sel: &ast.Ident{Name: alternative}, + }, + Args: []ast.Expr{m.State["target"].(ast.Expr)}, + } + + report.Report(pass, node, + fmt.Sprintf("%s is a type, not a function, and %s doesn't sort your values; consider using sort.%s instead", + typeName, + report.Render(pass, node.(*ast.AssignStmt).Rhs[0]), + alternative), + report.Fixes(edit.Fix(fmt.Sprintf("replace with call to sort.%s", alternative), edit.ReplaceWithNode(pass.Fset, node, r)))) + } + code.Preorder(pass, fn, (*ast.AssignStmt)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa4030/sa4030.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa4030/sa4030.go new file mode 100644 index 0000000000..e54edc2637 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa4030/sa4030.go @@ -0,0 +1,64 @@ +package sa4030 + +import ( + "fmt" + "go/ast" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA4030", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: "Ineffective attempt at generating random number", + Text: ` +Functions in the \'math/rand\' package that accept upper limits, such +as \'Intn\', generate random numbers in the half-open interval [0,n). In +other words, the generated numbers will be \'>= 0\' and \'< n\' – they +don't include \'n\'. \'rand.Intn(1)\' therefore doesn't generate \'0\' +or \'1\', it always generates \'0\'.`, + Since: "2022.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var ineffectiveRandIntQ = pattern.MustParse(` + (CallExpr + (Symbol + name@(Or + "math/rand.Int31n" + "math/rand.Int63n" + "math/rand.Intn" + "(*math/rand.Rand).Int31n" + "(*math/rand.Rand).Int63n" + "(*math/rand.Rand).Intn")) + [(IntegerLiteral "1")])`) + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + m, ok := code.Match(pass, ineffectiveRandIntQ, node) + if !ok { + return + } + + report.Report(pass, node, + fmt.Sprintf("%s(n) generates a random value 0 <= x < n; that is, the generated values don't include n; %s therefore always returns 0", + m.State["name"], report.Render(pass, node))) + } + + code.Preorder(pass, fn, (*ast.CallExpr)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa4031/sa4031.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa4031/sa4031.go new file mode 100644 index 0000000000..c7740b4a61 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa4031/sa4031.go @@ -0,0 +1,162 @@ +package sa4031 + +import ( + "fmt" + "go/ast" + "go/token" + "go/types" + "sort" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/ir" + "honnef.co/go/tools/internal/passes/buildir" + "honnef.co/go/tools/pattern" + "honnef.co/go/tools/staticcheck/sa4022" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA4031", + Run: run, + Requires: []*analysis.Analyzer{buildir.Analyzer, inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Checking never-nil value against nil`, + Since: "2022.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var allocationNilCheckQ = pattern.MustParse(`(IfStmt _ cond@(BinaryExpr lhs op@(Or "==" "!=") (Builtin "nil")) _ _)`) + +func run(pass *analysis.Pass) (interface{}, error) { + irpkg := pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg + + var path []ast.Node + fn := func(node ast.Node, stack []ast.Node) { + m, ok := code.Match(pass, allocationNilCheckQ, node) + if !ok { + return + } + cond := m.State["cond"].(ast.Node) + if _, ok := code.Match(pass, sa4022.CheckAddressIsNilQ, cond); ok { + // Don't duplicate diagnostics reported by SA4022 + return + } + lhs := m.State["lhs"].(ast.Expr) + path = path[:0] + for i := len(stack) - 1; i >= 0; i-- { + path = append(path, stack[i]) + } + irfn := ir.EnclosingFunction(irpkg, path) + if irfn == nil { + // For example for functions named "_", because we don't generate IR for them. + return + } + v, isAddr := irfn.ValueForExpr(lhs) + if isAddr { + return + } + + seen := map[ir.Value]struct{}{} + var values []ir.Value + var neverNil func(v ir.Value, track bool) bool + neverNil = func(v ir.Value, track bool) bool { + if _, ok := seen[v]; ok { + return true + } + seen[v] = struct{}{} + switch v := v.(type) { + case *ir.MakeClosure, *ir.Function: + if track { + values = append(values, v) + } + return true + case *ir.MakeChan, *ir.MakeMap, *ir.MakeSlice, *ir.Alloc: + if track { + values = append(values, v) + } + return true + case *ir.Slice: + if track { + values = append(values, v) + } + return neverNil(v.X, false) + case *ir.FieldAddr: + if track { + values = append(values, v) + } + return neverNil(v.X, false) + case *ir.Sigma: + return neverNil(v.X, true) + case *ir.Phi: + for _, e := range v.Edges { + if !neverNil(e, true) { + return false + } + } + return true + default: + return false + } + } + + if !neverNil(v, true) { + return + } + + var qualifier string + if op := m.State["op"].(token.Token); op == token.EQL { + qualifier = "never" + } else { + qualifier = "always" + } + fallback := fmt.Sprintf("this nil check is %s true", qualifier) + + sort.Slice(values, func(i, j int) bool { return values[i].Pos() < values[j].Pos() }) + + if ident, ok := m.State["lhs"].(*ast.Ident); ok { + if _, ok := pass.TypesInfo.ObjectOf(ident).(*types.Var); ok { + var opts []report.Option + if v.Parent() == irfn { + if len(values) == 1 { + opts = append(opts, report.Related(values[0], fmt.Sprintf("this is the value of %s", ident.Name))) + } else { + for _, vv := range values { + opts = append(opts, report.Related(vv, fmt.Sprintf("this is one of the value of %s", ident.Name))) + } + } + } + + switch v.(type) { + case *ir.MakeClosure, *ir.Function: + report.Report(pass, cond, "the checked variable contains a function and is never nil; did you mean to call it?", opts...) + default: + report.Report(pass, cond, fallback, opts...) + } + } else { + if _, ok := v.(*ir.Function); ok { + report.Report(pass, cond, "functions are never nil; did you mean to call it?") + } else { + report.Report(pass, cond, fallback) + } + } + } else { + if _, ok := v.(*ir.Function); ok { + report.Report(pass, cond, "functions are never nil; did you mean to call it?") + } else { + report.Report(pass, cond, fallback) + } + } + } + code.PreorderStack(pass, fn, (*ast.IfStmt)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa4032/sa4032.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa4032/sa4032.go new file mode 100644 index 0000000000..28dae9a056 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa4032/sa4032.go @@ -0,0 +1,218 @@ +package sa4032 + +import ( + "fmt" + "go/ast" + "go/build/constraint" + "go/constant" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/knowledge" + "honnef.co/go/tools/pattern" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA4032", + Run: CheckImpossibleGOOSGOARCH, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Comparing \'runtime.GOOS\' or \'runtime.GOARCH\' against impossible value`, + Since: "2024.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var ( + goosComparisonQ = pattern.MustParse(`(BinaryExpr (Symbol "runtime.GOOS") op@(Or "==" "!=") lit@(BasicLit "STRING" _))`) + goarchComparisonQ = pattern.MustParse(`(BinaryExpr (Symbol "runtime.GOARCH") op@(Or "==" "!=") lit@(BasicLit "STRING" _))`) +) + +func CheckImpossibleGOOSGOARCH(pass *analysis.Pass) (any, error) { + // TODO(dh): validate GOOS and GOARCH together. that is, + // given '(linux && amd64) || (windows && mips)', + // flag 'if runtime.GOOS == "linux" && runtime.GOARCH == "mips"' + // + // We can't use our IR for the control flow graph, because go/types constant folds constant comparisons, so + // 'runtime.GOOS == "windows"' will just become 'false'. We can't use the AST-based CFG builder from x/tools, + // because it doesn't model branch conditions. + + for _, f := range pass.Files { + expr, ok := code.BuildConstraints(pass, f) + if !ok { + continue + } + + ast.Inspect(f, func(node ast.Node) bool { + if m, ok := code.Match(pass, goosComparisonQ, node); ok { + tv := pass.TypesInfo.Types[m.State["lit"].(ast.Expr)] + goos := constant.StringVal(tv.Value) + + if _, ok := knowledge.KnownGOOS[goos]; !ok { + // Don't try to reason about GOOS values we don't know about. Maybe the user is using a newer + // version of Go that supports a new target, or maybe they run a fork of Go. + return true + } + sat, ok := validateGOOSComparison(expr, goos) + if !ok { + return true + } + if !sat { + // Note that we do not have to worry about constraints that can never be satisfied, such as 'linux + // && windows'. Packages with such files will not be passed to Staticcheck in the first place, + // precisely because the constraints aren't satisfiable. + report.Report(pass, node, + fmt.Sprintf("due to the file's build constraints, runtime.GOOS will never equal %q", goos)) + } + } else if m, ok := code.Match(pass, goarchComparisonQ, node); ok { + tv := pass.TypesInfo.Types[m.State["lit"].(ast.Expr)] + goarch := constant.StringVal(tv.Value) + + if _, ok := knowledge.KnownGOARCH[goarch]; !ok { + // Don't try to reason about GOARCH values we don't know about. Maybe the user is using a newer + // version of Go that supports a new target, or maybe they run a fork of Go. + return true + } + sat, ok := validateGOARCHComparison(expr, goarch) + if !ok { + return true + } + if !sat { + // Note that we do not have to worry about constraints that can never be satisfied, such as 'amd64 + // && mips'. Packages with such files will not be passed to Staticcheck in the first place, + // precisely because the constraints aren't satisfiable. + report.Report(pass, node, + fmt.Sprintf("due to the file's build constraints, runtime.GOARCH will never equal %q", goarch)) + } + } + return true + }) + } + + return nil, nil +} +func validateGOOSComparison(expr constraint.Expr, goos string) (sat bool, didCheck bool) { + matchGoosTag := func(tag string, goos string) (ok bool, goosTag bool) { + switch tag { + case "aix", + "android", + "dragonfly", + "freebsd", + "hurd", + "illumos", + "ios", + "js", + "netbsd", + "openbsd", + "plan9", + "wasip1", + "windows": + return goos == tag, true + case "darwin": + return (goos == "darwin" || goos == "ios"), true + case "linux": + return (goos == "linux" || goos == "android"), true + case "solaris": + return (goos == "solaris" || goos == "illumos"), true + case "unix": + return (goos == "aix" || + goos == "android" || + goos == "darwin" || + goos == "dragonfly" || + goos == "freebsd" || + goos == "hurd" || + goos == "illumos" || + goos == "ios" || + goos == "linux" || + goos == "netbsd" || + goos == "openbsd" || + goos == "solaris"), true + default: + return false, false + } + } + + return validateTagComparison(expr, func(tag string) (matched bool, special bool) { + return matchGoosTag(tag, goos) + }) +} + +func validateGOARCHComparison(expr constraint.Expr, goarch string) (sat bool, didCheck bool) { + matchGoarchTag := func(tag string, goarch string) (ok bool, goosTag bool) { + switch tag { + case "386", + "amd64", + "arm", + "arm64", + "loong64", + "mips", + "mipsle", + "mips64", + "mips64le", + "ppc64", + "ppc64le", + "riscv64", + "s390x", + "sparc64", + "wasm": + return goarch == tag, true + default: + return false, false + } + } + + return validateTagComparison(expr, func(tag string) (matched bool, special bool) { + return matchGoarchTag(tag, goarch) + }) +} + +func validateTagComparison(expr constraint.Expr, matchSpecialTag func(tag string) (matched bool, special bool)) (sat bool, didCheck bool) { + otherTags := map[string]int{} + // Collect all tags that aren't known architecture-based tags + b := expr.Eval(func(tag string) bool { + ok, special := matchSpecialTag(tag) + if !special { + // Assign an ID to this tag, but only if we haven't seen it before. For the expression 'foo && foo', this + // callback will be called twice for the 'foo' tag. + if _, ok := otherTags[tag]; !ok { + otherTags[tag] = len(otherTags) + } + } + return ok + }) + + if b || len(otherTags) == 0 { + // We're done. Either the formula can be satisfied regardless of the values of non-special tags, if any, + // or there aren't any non-special tags and the formula cannot be satisfied. + return b, true + } + + if len(otherTags) > 10 { + // We have to try 2**len(otherTags) combinations of tags. 2**10 is about the worst we're willing to try. + return false, false + } + + // Try all permutations of otherTags. If any evaluates to true, then the expression is satisfiable. + for bits := 0; bits < 1< 1 { + isGoFlagsTag := importsGoFlags && + (k == "choice" || k == "optional-value" || k == "default") + if !isGoFlagsTag { + report.Report(pass, field.Tag, fmt.Sprintf("duplicate struct tag %q", k)) + } + } + + switch k { + case "json": + checkJSONTag(pass, field, v[0]) + case "xml": + if _, err := fakexml.StructFieldInfo(rt.Field(i)); err != nil { + report.Report(pass, field.Tag, fmt.Sprintf("invalid XML tag: %s", err)) + } + checkXMLTag(pass, field, v[0]) + } + } + } + } + code.Preorder(pass, fn, (*ast.StructType)(nil)) + return nil, nil +} + +func checkJSONTag(pass *analysis.Pass, field *ast.Field, tag string) { + if pass.Pkg.Path() == "encoding/json" || pass.Pkg.Path() == "encoding/json_test" { + // don't flag malformed JSON tags in the encoding/json + // package; it knows what it is doing, and it is testing + // itself. + return + } + //lint:ignore SA9003 TODO(dh): should we flag empty tags? + if len(tag) == 0 { + } + if i := strings.Index(tag, ",format:"); i >= 0 { + tag = tag[:i] + } + fields := strings.Split(tag, ",") + for _, r := range fields[0] { + if !unicode.IsLetter(r) && !unicode.IsDigit(r) && !strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", r) { + report.Report(pass, field.Tag, fmt.Sprintf("invalid JSON field name %q", fields[0])) + } + } + options := make(map[string]int) + for _, s := range fields[1:] { + switch s { + case "": + // allow stuff like "-," + case "string": + // only for string, floating point, integer and bool + options[s]++ + tset := typeutil.NewTypeSet(pass.TypesInfo.TypeOf(field.Type)) + if len(tset.Terms) == 0 { + // TODO(dh): improve message, call out the use of type parameters + report.Report(pass, field.Tag, "the JSON string option only applies to fields of type string, floating point, integer or bool, or pointers to those") + continue + } + for _, term := range tset.Terms { + T := typeutil.Dereference(term.Type().Underlying()) + for _, term2 := range typeutil.NewTypeSet(T).Terms { + basic, ok := term2.Type().Underlying().(*types.Basic) + if !ok || (basic.Info()&(types.IsBoolean|types.IsInteger|types.IsFloat|types.IsString)) == 0 { + // TODO(dh): improve message, show how we arrived at the type + report.Report(pass, field.Tag, "the JSON string option only applies to fields of type string, floating point, integer or bool, or pointers to those") + } + } + } + case "omitzero", "omitempty", "nocase", "inline", "unknown": + options[s]++ + default: + report.Report(pass, field.Tag, fmt.Sprintf("unknown JSON option %q", s)) + } + } + var duplicates []string + for option, n := range options { + if n > 1 { + duplicates = append(duplicates, option) + } + } + if len(duplicates) > 0 { + sort.Strings(duplicates) + for _, option := range duplicates { + report.Report(pass, field.Tag, fmt.Sprintf("duplicate JSON option %q", option)) + } + } +} + +func checkXMLTag(pass *analysis.Pass, field *ast.Field, tag string) { + //lint:ignore SA9003 TODO(dh): should we flag empty tags? + if len(tag) == 0 { + } + fields := strings.Split(tag, ",") + counts := map[string]int{} + for _, s := range fields[1:] { + switch s { + case "attr", "chardata", "cdata", "innerxml", "comment": + counts[s]++ + case "omitempty", "any": + counts[s]++ + case "": + default: + report.Report(pass, field.Tag, fmt.Sprintf("invalid XML tag: unknown option %q", s)) + } + } + for k, v := range counts { + if v > 1 { + report.Report(pass, field.Tag, fmt.Sprintf("invalid XML tag: duplicate option %q", k)) + } + } +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/structtag.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa5008/structtag.go similarity index 98% rename from tools/vendor/honnef.co/go/tools/staticcheck/structtag.go rename to tools/vendor/honnef.co/go/tools/staticcheck/sa5008/structtag.go index 38830a22c6..e8121e5084 100644 --- a/tools/vendor/honnef.co/go/tools/staticcheck/structtag.go +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa5008/structtag.go @@ -1,7 +1,7 @@ // Copyright 2009 The Go Authors. All rights reserved. // Copyright 2019 Dominik Honnef. All rights reserved. -package staticcheck +package sa5008 import "strconv" diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa5009/sa5009.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa5009/sa5009.go new file mode 100644 index 0000000000..4375c7fdcd --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa5009/sa5009.go @@ -0,0 +1,409 @@ +package sa5009 + +import ( + "fmt" + "go/constant" + "go/types" + + "honnef.co/go/tools/analysis/callcheck" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/go/ir" + "honnef.co/go/tools/go/ir/irutil" + "honnef.co/go/tools/go/types/typeutil" + "honnef.co/go/tools/internal/passes/buildir" + "honnef.co/go/tools/knowledge" + "honnef.co/go/tools/printf" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA5009", + Requires: []*analysis.Analyzer{buildir.Analyzer}, + Run: callcheck.Analyzer(rules), + }, + Doc: &lint.RawDocumentation{ + Title: `Invalid Printf call`, + Since: "2019.2", + Severity: lint.SeverityError, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +// TODO(dh): detect printf wrappers +var rules = map[string]callcheck.Check{ + "fmt.Errorf": func(call *callcheck.Call) { check(call, 0, 1) }, + "fmt.Printf": func(call *callcheck.Call) { check(call, 0, 1) }, + "fmt.Sprintf": func(call *callcheck.Call) { check(call, 0, 1) }, + "fmt.Fprintf": func(call *callcheck.Call) { check(call, 1, 2) }, + "golang.org/x/xerrors.Errorf": func(call *callcheck.Call) { check(call, 0, 1) }, +} + +type verbFlag int + +const ( + isInt verbFlag = 1 << iota + isBool + isFP + isString + isPointer + // Verbs that accept "pseudo pointers" will sometimes dereference + // non-nil pointers. For example, %x on a non-nil *struct will print the + // individual fields, but on a nil pointer it will print the address. + isPseudoPointer + isSlice + isAny + noRecurse +) + +var verbs = [...]verbFlag{ + 'b': isPseudoPointer | isInt | isFP, + 'c': isInt, + 'd': isPseudoPointer | isInt, + 'e': isFP, + 'E': isFP, + 'f': isFP, + 'F': isFP, + 'g': isFP, + 'G': isFP, + 'o': isPseudoPointer | isInt, + 'O': isPseudoPointer | isInt, + 'p': isSlice | isPointer | noRecurse, + 'q': isInt | isString, + 's': isString, + 't': isBool, + 'T': isAny, + 'U': isInt, + 'v': isAny, + 'X': isPseudoPointer | isInt | isFP | isString, + 'x': isPseudoPointer | isInt | isFP | isString, +} + +func check(call *callcheck.Call, fIdx, vIdx int) { + f := call.Args[fIdx] + var args []ir.Value + switch v := call.Args[vIdx].Value.Value.(type) { + case *ir.Slice: + var ok bool + args, ok = irutil.Vararg(v) + if !ok { + // We don't know what the actual arguments to the function are + return + } + case *ir.Const: + // nil, i.e. no arguments + default: + // We don't know what the actual arguments to the function are + return + } + checkImpl(f, f.Value.Value, args) +} + +func checkImpl(carg *callcheck.Argument, f ir.Value, args []ir.Value) { + var msCache *typeutil.MethodSetCache + if f.Parent() != nil { + msCache = &f.Parent().Prog.MethodSets + } + + elem := func(T types.Type, verb rune) ([]types.Type, bool) { + if verbs[verb]&noRecurse != 0 { + return []types.Type{T}, false + } + switch T := T.(type) { + case *types.Slice: + if verbs[verb]&isSlice != 0 { + return []types.Type{T}, false + } + if verbs[verb]&isString != 0 && types.Identical(T.Elem().Underlying(), types.Typ[types.Byte]) { + return []types.Type{T}, false + } + return []types.Type{T.Elem()}, true + case *types.Map: + key := T.Key() + val := T.Elem() + return []types.Type{key, val}, true + case *types.Struct: + out := make([]types.Type, 0, T.NumFields()) + for i := 0; i < T.NumFields(); i++ { + out = append(out, T.Field(i).Type()) + } + return out, true + case *types.Array: + return []types.Type{T.Elem()}, true + default: + return []types.Type{T}, false + } + } + isInfo := func(T types.Type, info types.BasicInfo) bool { + basic, ok := T.Underlying().(*types.Basic) + return ok && basic.Info()&info != 0 + } + + isFormatter := func(T types.Type, ms *types.MethodSet) bool { + sel := ms.Lookup(nil, "Format") + if sel == nil { + return false + } + fn, ok := sel.Obj().(*types.Func) + if !ok { + // should be unreachable + return false + } + sig := fn.Type().(*types.Signature) + if sig.Params().Len() != 2 { + return false + } + // TODO(dh): check the types of the arguments for more + // precision + if sig.Results().Len() != 0 { + return false + } + return true + } + + var seen typeutil.Map[struct{}] + var checkType func(verb rune, T types.Type, top bool) bool + checkType = func(verb rune, T types.Type, top bool) bool { + if top { + seen = typeutil.Map[struct{}]{} + } + if _, ok := seen.At(T); ok { + return true + } + seen.Set(T, struct{}{}) + if int(verb) >= len(verbs) { + // Unknown verb + return true + } + + flags := verbs[verb] + if flags == 0 { + // Unknown verb + return true + } + + ms := msCache.MethodSet(T) + if isFormatter(T, ms) { + // the value is responsible for formatting itself + return true + } + + if flags&isString != 0 && (types.Implements(T, knowledge.Interfaces["fmt.Stringer"]) || types.Implements(T, knowledge.Interfaces["error"])) { + // Check for stringer early because we're about to dereference + return true + } + + T = T.Underlying() + if flags&(isPointer|isPseudoPointer) == 0 && top { + T = typeutil.Dereference(T) + } + if flags&isPseudoPointer != 0 && top { + t := typeutil.Dereference(T) + if _, ok := t.Underlying().(*types.Struct); ok { + T = t + } + } + + if _, ok := T.(*types.Interface); ok { + // We don't know what's in the interface + return true + } + + var info types.BasicInfo + if flags&isInt != 0 { + info |= types.IsInteger + } + if flags&isBool != 0 { + info |= types.IsBoolean + } + if flags&isFP != 0 { + info |= types.IsFloat | types.IsComplex + } + if flags&isString != 0 { + info |= types.IsString + } + + if info != 0 && isInfo(T, info) { + return true + } + + if flags&isString != 0 { + isStringyElem := func(typ types.Type) bool { + if typ, ok := typ.Underlying().(*types.Basic); ok { + return typ.Kind() == types.Byte + } + return false + } + switch T := T.(type) { + case *types.Slice: + if isStringyElem(T.Elem()) { + return true + } + case *types.Array: + if isStringyElem(T.Elem()) { + return true + } + } + if types.Implements(T, knowledge.Interfaces["fmt.Stringer"]) || types.Implements(T, knowledge.Interfaces["error"]) { + return true + } + } + + if flags&isPointer != 0 && typeutil.IsPointerLike(T) { + return true + } + if flags&isPseudoPointer != 0 { + switch U := T.Underlying().(type) { + case *types.Pointer: + if !top { + return true + } + + if _, ok := U.Elem().Underlying().(*types.Struct); !ok { + // TODO(dh): can this condition ever be false? For + // *T, if T is a struct, we'll already have + // dereferenced it, meaning the *types.Pointer + // branch couldn't have been taken. For T that + // aren't structs, this condition will always + // evaluate to true. + return true + } + case *types.Chan, *types.Signature: + // Channels and functions are always treated as + // pointers and never recursed into. + return true + case *types.Basic: + if U.Kind() == types.UnsafePointer { + return true + } + case *types.Interface: + // we will already have bailed if the type is an + // interface. + panic("unreachable") + default: + // other pointer-like types, such as maps or slices, + // will be printed element-wise. + } + } + + if flags&isSlice != 0 { + if _, ok := T.(*types.Slice); ok { + return true + } + } + + if flags&isAny != 0 { + return true + } + + elems, ok := elem(T.Underlying(), verb) + if !ok { + return false + } + for _, elem := range elems { + if !checkType(verb, elem, false) { + return false + } + } + + return true + } + + k, ok := irutil.Flatten(f).(*ir.Const) + if !ok { + return + } + actions, err := printf.Parse(constant.StringVal(k.Value)) + if err != nil { + carg.Invalid("couldn't parse format string") + return + } + + ptr := 1 + hasExplicit := false + + checkStar := func(verb printf.Verb, star printf.Argument) bool { + if star, ok := star.(printf.Star); ok { + idx := 0 + if star.Index == -1 { + idx = ptr + ptr++ + } else { + hasExplicit = true + idx = star.Index + ptr = star.Index + 1 + } + if idx == 0 { + carg.Invalid(fmt.Sprintf("Printf format %s reads invalid arg 0; indices are 1-based", verb.Raw)) + return false + } + if idx > len(args) { + carg.Invalid( + fmt.Sprintf("Printf format %s reads arg #%d, but call has only %d args", + verb.Raw, idx, len(args))) + return false + } + if arg, ok := args[idx-1].(*ir.MakeInterface); ok { + if !isInfo(arg.X.Type(), types.IsInteger) { + carg.Invalid(fmt.Sprintf("Printf format %s reads non-int arg #%d as argument of *", verb.Raw, idx)) + } + } + } + return true + } + + // We only report one problem per format string. Making a + // mistake with an index tends to invalidate all future + // implicit indices. + for _, action := range actions { + verb, ok := action.(printf.Verb) + if !ok { + continue + } + + if !checkStar(verb, verb.Width) || !checkStar(verb, verb.Precision) { + return + } + + off := ptr + if verb.Value != -1 { + hasExplicit = true + off = verb.Value + } + if off > len(args) { + carg.Invalid( + fmt.Sprintf("Printf format %s reads arg #%d, but call has only %d args", + verb.Raw, off, len(args))) + return + } else if verb.Value == 0 && verb.Letter != '%' { + carg.Invalid(fmt.Sprintf("Printf format %s reads invalid arg 0; indices are 1-based", verb.Raw)) + return + } else if off != 0 { + arg, ok := args[off-1].(*ir.MakeInterface) + if ok { + if !checkType(verb.Letter, arg.X.Type(), true) { + carg.Invalid(fmt.Sprintf("Printf format %s has arg #%d of wrong type %s", + verb.Raw, ptr, args[ptr-1].(*ir.MakeInterface).X.Type())) + return + } + } + } + + switch verb.Value { + case -1: + // Consume next argument + ptr++ + case 0: + // Don't consume any arguments + default: + ptr = verb.Value + 1 + } + } + + if !hasExplicit && ptr <= len(args) { + carg.Invalid(fmt.Sprintf("Printf call needs %d args but has %d args", ptr-1, len(args))) + } +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa5010/sa5010.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa5010/sa5010.go new file mode 100644 index 0000000000..49c8ec2872 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa5010/sa5010.go @@ -0,0 +1,106 @@ +package sa5010 + +import ( + "fmt" + "go/types" + + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/ir" + "honnef.co/go/tools/internal/passes/buildir" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA5010", + Run: run, + Requires: []*analysis.Analyzer{buildir.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Impossible type assertion`, + + Text: `Some type assertions can be statically proven to be +impossible. This is the case when the method sets of both +arguments of the type assertion conflict with each other, for +example by containing the same method with different +signatures. + +The Go compiler already applies this check when asserting from an +interface value to a concrete type. If the concrete type misses +methods from the interface, or if function signatures don't match, +then the type assertion can never succeed. + +This check applies the same logic when asserting from one interface to +another. If both interface types contain the same method but with +different signatures, then the type assertion can never succeed, +either.`, + + Since: "2020.1", + Severity: lint.SeverityWarning, + // Technically this should be MergeIfAll, but the Go compiler + // already flags some impossible type assertions, so + // MergeIfAny is consistent with the compiler. + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + type entry struct { + l, r *types.Func + } + + msc := &pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg.Prog.MethodSets + for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { + for _, b := range fn.Blocks { + for _, instr := range b.Instrs { + assert, ok := instr.(*ir.TypeAssert) + if !ok { + continue + } + var wrong []entry + left := assert.X.Type() + right := assert.AssertedType + righti, ok := right.Underlying().(*types.Interface) + + if !ok { + // We only care about interface->interface + // assertions. The Go compiler already catches + // impossible interface->concrete assertions. + continue + } + + ms := msc.MethodSet(left) + for i := 0; i < righti.NumMethods(); i++ { + mr := righti.Method(i).Origin() + sel := ms.Lookup(mr.Pkg(), mr.Name()) + if sel == nil { + continue + } + ml := sel.Obj().(*types.Func).Origin() + if types.AssignableTo(ml.Type(), mr.Type()) { + continue + } + + wrong = append(wrong, entry{ml, mr}) + } + + if len(wrong) != 0 { + s := fmt.Sprintf("impossible type assertion; %s and %s contradict each other:", + types.TypeString(left, types.RelativeTo(pass.Pkg)), + types.TypeString(right, types.RelativeTo(pass.Pkg))) + for _, e := range wrong { + s += fmt.Sprintf("\n\twrong type for %s method", e.l.Name()) + s += fmt.Sprintf("\n\t\thave %s", e.l.Type()) + s += fmt.Sprintf("\n\t\twant %s", e.r.Type()) + } + report.Report(pass, assert, s) + } + } + } + } + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa5011/sa5011.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa5011/sa5011.go new file mode 100644 index 0000000000..9f2f757995 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa5011/sa5011.go @@ -0,0 +1,221 @@ +package sa5011 + +import ( + "go/types" + + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/ir" + "honnef.co/go/tools/go/types/typeutil" + "honnef.co/go/tools/internal/passes/buildir" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA5011", + Run: run, + Requires: []*analysis.Analyzer{buildir.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Possible nil pointer dereference`, + + Text: `A pointer is being dereferenced unconditionally, while +also being checked against nil in another place. This suggests that +the pointer may be nil and dereferencing it may panic. This is +commonly a result of improperly ordered code or missing return +statements. Consider the following examples: + + func fn(x *int) { + fmt.Println(*x) + + // This nil check is equally important for the previous dereference + if x != nil { + foo(*x) + } + } + + func TestFoo(t *testing.T) { + x := compute() + if x == nil { + t.Errorf("nil pointer received") + } + + // t.Errorf does not abort the test, so if x is nil, the next line will panic. + foo(*x) + } + +Staticcheck tries to deduce which functions abort control flow. +For example, it is aware that a function will not continue +execution after a call to \'panic\' or \'log.Fatal\'. However, sometimes +this detection fails, in particular in the presence of +conditionals. Consider the following example: + + func Log(msg string, level int) { + fmt.Println(msg) + if level == levelFatal { + os.Exit(1) + } + } + + func Fatal(msg string) { + Log(msg, levelFatal) + } + + func fn(x *int) { + if x == nil { + Fatal("unexpected nil pointer") + } + fmt.Println(*x) + } + +Staticcheck will flag the dereference of \'x\', even though it is perfectly +safe. Staticcheck is not able to deduce that a call to +Fatal will exit the program. For the time being, the easiest +workaround is to modify the definition of Fatal like so: + + func Fatal(msg string) { + Log(msg, levelFatal) + panic("unreachable") + } + +We also hard-code functions from common logging packages such as +logrus. Please file an issue if we're missing support for a +popular package.`, + Since: "2020.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + // This is an extremely trivial check that doesn't try to reason + // about control flow. That is, phis and sigmas do not propagate + // any information. As such, we can flag this: + // + // _ = *x + // if x == nil { return } + // + // but we cannot flag this: + // + // if x == nil { println(x) } + // _ = *x + // + // but we can flag this, because the if's body doesn't use x: + // + // if x == nil { println("this is bad") } + // _ = *x + // + // nor many other variations of conditional uses of or assignments to x. + // + // However, even this trivial implementation finds plenty of + // real-world bugs, such as dereference before nil pointer check, + // or using t.Error instead of t.Fatal when encountering nil + // pointers. + // + // On the flip side, our naive implementation avoids false positives in branches, such as + // + // if x != nil { _ = *x } + // + // due to the same lack of propagating information through sigma + // nodes. x inside the branch will be independent of the x in the + // nil pointer check. + // + // + // We could implement a more powerful check, but then we'd be + // getting false positives instead of false negatives because + // we're incapable of deducing relationships between variables. + // For example, a function might return a pointer and an error, + // and the error being nil guarantees that the pointer is not nil. + // Depending on the surrounding code, the pointer may still end up + // being checked against nil in one place, and guarded by a check + // on the error in another, which would lead to us marking some + // loads as unsafe. + // + // Unfortunately, simply hard-coding the relationship between + // return values wouldn't eliminate all false positives, either. + // Many other more subtle relationships exist. An abridged example + // from real code: + // + // if a == nil && b == nil { return } + // c := fn(a) + // if c != "" { _ = *a } + // + // where `fn` is guaranteed to return a non-empty string if a + // isn't nil. + // + // We choose to err on the side of false negatives. + + isNilConst := func(v ir.Value) bool { + if typeutil.IsPointerLike(v.Type()) { + if k, ok := v.(*ir.Const); ok { + return k.IsNil() + } + } + return false + } + + for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { + maybeNil := map[ir.Value]ir.Instruction{} + for _, b := range fn.Blocks { + for _, instr := range b.Instrs { + // Originally we looked at all ir.BinOp, but that would lead to calls like 'assert(x != nil)' causing false positives. + // Restrict ourselves to actual if statements, as these are more likely to affect control flow in a way we can observe. + if instr, ok := instr.(*ir.If); ok { + if cond, ok := instr.Cond.(*ir.BinOp); ok { + if isNilConst(cond.X) { + maybeNil[cond.Y] = cond + } + if isNilConst(cond.Y) { + maybeNil[cond.X] = cond + } + } + } + } + } + + for _, b := range fn.Blocks { + for _, instr := range b.Instrs { + var ptr ir.Value + switch instr := instr.(type) { + case *ir.Load: + ptr = instr.X + case *ir.Store: + ptr = instr.Addr + case *ir.IndexAddr: + ptr = instr.X + if typeutil.All(ptr.Type(), func(term *types.Term) bool { + if _, ok := term.Type().Underlying().(*types.Slice); ok { + return true + } + return false + }) { + // indexing a nil slice does not cause a nil pointer panic + // + // Note: This also works around the bad lowering of range loops over slices + // (https://github.com/dominikh/go-tools/issues/1053) + continue + } + case *ir.FieldAddr: + ptr = instr.X + } + if ptr != nil { + switch ptr.(type) { + case *ir.Alloc, *ir.FieldAddr, *ir.IndexAddr: + // these cannot be nil + continue + } + if r, ok := maybeNil[ptr]; ok { + report.Report(pass, instr, "possible nil pointer dereference", + report.Related(r, "this check suggests that the pointer can be nil")) + } + } + } + } + } + + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa5012/sa5012.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa5012/sa5012.go new file mode 100644 index 0000000000..350cf43d9c --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa5012/sa5012.go @@ -0,0 +1,286 @@ +package sa5012 + +import ( + "fmt" + "go/ast" + "go/constant" + "go/token" + "go/types" + + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/ir" + "honnef.co/go/tools/go/ir/irutil" + "honnef.co/go/tools/go/types/typeutil" + "honnef.co/go/tools/internal/passes/buildir" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA5012", + Run: run, + FactTypes: []analysis.Fact{new(evenElements)}, + Requires: []*analysis.Analyzer{buildir.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: "Passing odd-sized slice to function expecting even size", + Text: `Some functions that take slices as parameters expect the slices to have an even number of elements. +Often, these functions treat elements in a slice as pairs. +For example, \'strings.NewReplacer\' takes pairs of old and new strings, +and calling it with an odd number of elements would be an error.`, + Since: "2020.2", + Severity: lint.SeverityError, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +type evenElements struct{} + +func (evenElements) AFact() {} + +func (evenElements) String() string { return "needs even elements" } + +func findSliceLength(v ir.Value) int { + // TODO(dh): VRP would help here + + v = irutil.Flatten(v) + val := func(v ir.Value) int { + if v, ok := v.(*ir.Const); ok { + return int(v.Int64()) + } + return -1 + } + switch v := v.(type) { + case *ir.Slice: + low := 0 + high := -1 + if v.Low != nil { + low = val(v.Low) + } + if v.High != nil { + high = val(v.High) + } else { + switch vv := v.X.(type) { + case *ir.Alloc: + high = int(typeutil.Dereference(vv.Type()).Underlying().(*types.Array).Len()) + case *ir.Slice: + high = findSliceLength(vv) + } + } + if low == -1 || high == -1 { + return -1 + } + return high - low + default: + return -1 + } +} + +func flagSliceLens(pass *analysis.Pass) { + var tag evenElements + + for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { + for _, b := range fn.Blocks { + for _, instr := range b.Instrs { + call, ok := instr.(ir.CallInstruction) + if !ok { + continue + } + callee := call.Common().StaticCallee() + if callee == nil { + continue + } + for argi, arg := range call.Common().Args { + if callee.Signature.Recv() != nil { + if argi == 0 { + continue + } + argi-- + } + + _, ok := arg.Type().Underlying().(*types.Slice) + if !ok { + continue + } + param := callee.Signature.Params().At(argi) + if !pass.ImportObjectFact(param, &tag) { + continue + } + + // TODO handle stubs + + // we know the argument has to have even length. + // now let's try to find its length + if n := findSliceLength(arg); n > -1 && n%2 != 0 { + src := call.Source().(*ast.CallExpr).Args[argi] + sig := call.Common().Signature() + var label string + if argi == sig.Params().Len()-1 && sig.Variadic() { + label = "variadic argument" + } else { + label = "argument" + } + // Note that param.Name() is guaranteed to not + // be empty, otherwise the function couldn't + // have enforced its length. + report.Report(pass, src, fmt.Sprintf("%s %q is expected to have even number of elements, but has %d elements", label, param.Name(), n)) + } + } + } + } + } +} + +func findSliceLenChecks(pass *analysis.Pass) { + // mark all function parameters that have to be of even length + for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { + for _, b := range fn.Blocks { + // all paths go through this block + if !b.Dominates(fn.Exit) { + continue + } + + // if foo % 2 != 0 + ifi, ok := b.Control().(*ir.If) + if !ok { + continue + } + cmp, ok := ifi.Cond.(*ir.BinOp) + if !ok { + continue + } + var needle uint64 + switch cmp.Op { + case token.NEQ: + // look for != 0 + needle = 0 + case token.EQL: + // look for == 1 + needle = 1 + default: + continue + } + + rem, ok1 := cmp.X.(*ir.BinOp) + k, ok2 := cmp.Y.(*ir.Const) + if ok1 != ok2 { + continue + } + if !ok1 { + rem, ok1 = cmp.Y.(*ir.BinOp) + k, ok2 = cmp.X.(*ir.Const) + } + if !ok1 || !ok2 || rem.Op != token.REM || k.Value.Kind() != constant.Int || k.Uint64() != needle { + continue + } + k, ok = rem.Y.(*ir.Const) + if !ok || k.Value.Kind() != constant.Int || k.Uint64() != 2 { + continue + } + + // if len(foo) % 2 != 0 + call, ok := rem.X.(*ir.Call) + if !ok || !irutil.IsCallTo(call.Common(), "len") { + continue + } + + // we're checking the length of a parameter that is a slice + // TODO(dh): support parameters that have flown through sigmas and phis + param, ok := call.Call.Args[0].(*ir.Parameter) + if !ok { + continue + } + if !typeutil.All(param.Type(), typeutil.IsSlice) { + continue + } + + // if len(foo) % 2 != 0 then panic + if _, ok := b.Succs[0].Control().(*ir.Panic); !ok { + continue + } + + pass.ExportObjectFact(param.Object(), new(evenElements)) + } + } +} + +func findIndirectSliceLenChecks(pass *analysis.Pass) { + seen := map[*ir.Function]struct{}{} + + var doFunction func(fn *ir.Function) + doFunction = func(fn *ir.Function) { + if _, ok := seen[fn]; ok { + return + } + seen[fn] = struct{}{} + + for _, b := range fn.Blocks { + // all paths go through this block + if !b.Dominates(fn.Exit) { + continue + } + + for _, instr := range b.Instrs { + call, ok := instr.(*ir.Call) + if !ok { + continue + } + callee := call.Call.StaticCallee() + if callee == nil { + continue + } + + if callee.Pkg == fn.Pkg || callee.Pkg == nil { + doFunction(callee) + } + + for argi, arg := range call.Call.Args { + if callee.Signature.Recv() != nil { + if argi == 0 { + continue + } + argi-- + } + + // TODO(dh): support parameters that have flown through length-preserving instructions + param, ok := arg.(*ir.Parameter) + if !ok { + continue + } + if !typeutil.All(param.Type(), typeutil.IsSlice) { + continue + } + + // We can't use callee.Params to look up the + // parameter, because Params is not populated for + // external functions. In our modular analysis. + // any function in any package that isn't the + // current package is considered "external", as it + // has been loaded from export data only. + sigParams := callee.Signature.Params() + + if !pass.ImportObjectFact(sigParams.At(argi), new(evenElements)) { + continue + } + pass.ExportObjectFact(param.Object(), new(evenElements)) + } + } + } + } + + for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { + doFunction(fn) + } +} + +func run(pass *analysis.Pass) (interface{}, error) { + findSliceLenChecks(pass) + findIndirectSliceLenChecks(pass) + flagSliceLens(pass) + + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa6000/sa6000.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa6000/sa6000.go new file mode 100644 index 0000000000..958cc8ff5a --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa6000/sa6000.go @@ -0,0 +1,57 @@ +package sa6000 + +import ( + "fmt" + + "honnef.co/go/tools/analysis/callcheck" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/go/ir" + "honnef.co/go/tools/go/ir/irutil" + "honnef.co/go/tools/internal/passes/buildir" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA6000", + Requires: []*analysis.Analyzer{buildir.Analyzer}, + Run: callcheck.Analyzer(rules), + }, + Doc: &lint.RawDocumentation{ + Title: `Using \'regexp.Match\' or related in a loop, should use \'regexp.Compile\'`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var rules = map[string]callcheck.Check{ + "regexp.Match": check("regexp.Match"), + "regexp.MatchReader": check("regexp.MatchReader"), + "regexp.MatchString": check("regexp.MatchString"), +} + +func check(name string) callcheck.Check { + return func(call *callcheck.Call) { + if callcheck.ExtractConst(call.Args[0].Value) == nil { + return + } + if !isInLoop(call.Instr.Block()) { + return + } + call.Invalid(fmt.Sprintf("calling %s in a loop has poor performance, consider using regexp.Compile", name)) + } +} + +func isInLoop(b *ir.BasicBlock) bool { + sets := irutil.FindLoops(b.Parent()) + for _, set := range sets { + if set.Has(b) { + return true + } + } + return false +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa6001/sa6001.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa6001/sa6001.go new file mode 100644 index 0000000000..ae19f02970 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa6001/sa6001.go @@ -0,0 +1,124 @@ +package sa6001 + +import ( + "go/ast" + "go/types" + + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/ir" + "honnef.co/go/tools/go/types/typeutil" + "honnef.co/go/tools/internal/passes/buildir" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA6001", + Run: run, + Requires: []*analysis.Analyzer{buildir.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Missing an optimization opportunity when indexing maps by byte slices`, + + Text: `Map keys must be comparable, which precludes the use of byte slices. +This usually leads to using string keys and converting byte slices to +strings. + +Normally, a conversion of a byte slice to a string needs to copy the data and +causes allocations. The compiler, however, recognizes \'m[string(b)]\' and +uses the data of \'b\' directly, without copying it, because it knows that +the data can't change during the map lookup. This leads to the +counter-intuitive situation that + + k := string(b) + println(m[k]) + println(m[k]) + +will be less efficient than + + println(m[string(b)]) + println(m[string(b)]) + +because the first version needs to copy and allocate, while the second +one does not. + +For some history on this optimization, check out commit +f5f5a8b6209f84961687d993b93ea0d397f5d5bf in the Go repository.`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { + for _, b := range fn.Blocks { + insLoop: + for _, ins := range b.Instrs { + var fromType types.Type + var toType types.Type + + // find []byte -> string conversions + switch ins := ins.(type) { + case *ir.Convert: + fromType = ins.X.Type() + toType = ins.Type() + case *ir.MultiConvert: + fromType = ins.X.Type() + toType = ins.Type() + default: + continue + } + if toType != types.Universe.Lookup("string").Type() { + continue + } + tset := typeutil.NewTypeSet(fromType) + // If at least one of the types is []byte, then it's more efficient to inline the conversion + if !tset.Any(func(term *types.Term) bool { + s, ok := term.Type().Underlying().(*types.Slice) + return ok && s.Elem().Underlying() == types.Universe.Lookup("byte").Type() + }) { + continue + } + refs := ins.Referrers() + // need at least two (DebugRef) references: the + // conversion and the *ast.Ident + if refs == nil || len(*refs) < 2 { + continue + } + ident := false + // skip first reference, that's the conversion itself + for _, ref := range (*refs)[1:] { + switch ref := ref.(type) { + case *ir.DebugRef: + if _, ok := ref.Expr.(*ast.Ident); !ok { + // the string seems to be used somewhere + // unexpected; the default branch should + // catch this already, but be safe + continue insLoop + } else { + ident = true + } + case *ir.MapLookup: + default: + // the string is used somewhere else than a + // map lookup + continue insLoop + } + } + + // the result of the conversion wasn't assigned to an + // identifier + if !ident { + continue + } + report.Report(pass, ins, "m[string(key)] would be more efficient than k := string(key); m[k]") + } + } + } + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa6002/sa6002.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa6002/sa6002.go new file mode 100644 index 0000000000..91ec73030a --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa6002/sa6002.go @@ -0,0 +1,52 @@ +package sa6002 + +import ( + "go/types" + + "honnef.co/go/tools/analysis/callcheck" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/go/types/typeutil" + "honnef.co/go/tools/internal/passes/buildir" + "honnef.co/go/tools/knowledge" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA6002", + Requires: []*analysis.Analyzer{buildir.Analyzer}, + Run: callcheck.Analyzer(rules), + }, + Doc: &lint.RawDocumentation{ + Title: `Storing non-pointer values in \'sync.Pool\' allocates memory`, + Text: `A \'sync.Pool\' is used to avoid unnecessary allocations and reduce the +amount of work the garbage collector has to do. + +When passing a value that is not a pointer to a function that accepts +an interface, the value needs to be placed on the heap, which means an +additional allocation. Slices are a common thing to put in sync.Pools, +and they're structs with 3 fields (length, capacity, and a pointer to +an array). In order to avoid the extra allocation, one should store a +pointer to the slice instead. + +See the comments on https://go-review.googlesource.com/c/go/+/24371 +that discuss this problem.`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var rules = map[string]callcheck.Check{ + "(*sync.Pool).Put": func(call *callcheck.Call) { + arg := call.Args[knowledge.Arg("(*sync.Pool).Put.x")] + typ := arg.Value.Value.Type() + _, isSlice := typ.Underlying().(*types.Slice) + if !typeutil.IsPointerLike(typ) || isSlice { + arg.Invalid("argument should be pointer-like to avoid allocations") + } + }, +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa6003/sa6003.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa6003/sa6003.go new file mode 100644 index 0000000000..271ee9f0af --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa6003/sa6003.go @@ -0,0 +1,42 @@ +package sa6003 + +import ( + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/internal/passes/buildir" + "honnef.co/go/tools/internal/sharedcheck" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA6003", + Run: sharedcheck.CheckRangeStringRunes, + Requires: []*analysis.Analyzer{buildir.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Converting a string to a slice of runes before ranging over it`, + Text: `You may want to loop over the runes in a string. Instead of converting +the string to a slice of runes and looping over that, you can loop +over the string itself. That is, + + for _, r := range s {} + +and + + for _, r := range []rune(s) {} + +will yield the same values. The first version, however, will be faster +and avoid unnecessary memory allocations. + +Do note that if you are interested in the indices, ranging over a +string and over a slice of runes will yield different indices. The +first one yields byte offsets, while the second one yields indices in +the slice of runes.`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa6005/sa6005.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa6005/sa6005.go new file mode 100644 index 0000000000..f7c151198b --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa6005/sa6005.go @@ -0,0 +1,80 @@ +package sa6005 + +import ( + "go/ast" + "go/token" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/edit" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA6005", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Inefficient string comparison with \'strings.ToLower\' or \'strings.ToUpper\'`, + Text: `Converting two strings to the same case and comparing them like so + + if strings.ToLower(s1) == strings.ToLower(s2) { + ... + } + +is significantly more expensive than comparing them with +\'strings.EqualFold(s1, s2)\'. This is due to memory usage as well as +computational complexity. + +\'strings.ToLower\' will have to allocate memory for the new strings, as +well as convert both strings fully, even if they differ on the very +first byte. strings.EqualFold, on the other hand, compares the strings +one character at a time. It doesn't need to create two intermediate +strings and can return as soon as the first non-matching character has +been found. + +For a more in-depth explanation of this issue, see +https://blog.digitalocean.com/how-to-efficiently-compare-strings-in-go/`, + Since: "2019.2", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var ( + checkToLowerToUpperComparisonQ = pattern.MustParse(` + (BinaryExpr + (CallExpr fun@(Symbol (Or "strings.ToLower" "strings.ToUpper")) [a]) + tok@(Or "==" "!=") + (CallExpr fun [b]))`) + checkToLowerToUpperComparisonR = pattern.MustParse(`(CallExpr (SelectorExpr (Ident "strings") (Ident "EqualFold")) [a b])`) +) + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + m, ok := code.Match(pass, checkToLowerToUpperComparisonQ, node) + if !ok { + return + } + rn := pattern.NodeToAST(checkToLowerToUpperComparisonR.Root, m.State).(ast.Expr) + if m.State["tok"].(token.Token) == token.NEQ { + rn = &ast.UnaryExpr{ + Op: token.NOT, + X: rn, + } + } + + report.Report(pass, node, "should use strings.EqualFold instead", report.Fixes(edit.Fix("replace with strings.EqualFold", edit.ReplaceWithNode(pass.Fset, node, rn)))) + } + + code.Preorder(pass, fn, (*ast.BinaryExpr)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa6006/sa6006.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa6006/sa6006.go new file mode 100644 index 0000000000..768905ab45 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa6006/sa6006.go @@ -0,0 +1,53 @@ +package sa6006 + +import ( + "go/ast" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA6006", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Using io.WriteString to write \'[]byte\'`, + Text: `Using io.WriteString to write a slice of bytes, as in + + io.WriteString(w, string(b)) + +is both unnecessary and inefficient. Converting from \'[]byte\' to \'string\' +has to allocate and copy the data, and we could simply use \'w.Write(b)\' +instead.`, + + Since: "2024.1", + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var ioWriteStringConversion = pattern.MustParse(`(CallExpr (Symbol "io.WriteString") [_ (CallExpr (Builtin "string") [arg])])`) + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + m, ok := code.Match(pass, ioWriteStringConversion, node) + if !ok { + return + } + if !code.IsOfStringConvertibleByteSlice(pass, m.State["arg"].(ast.Expr)) { + return + } + report.Report(pass, node, "use io.Writer.Write instead of converting from []byte to string to use io.WriteString") + } + code.Preorder(pass, fn, (*ast.CallExpr)(nil)) + + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa9001/sa9001.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa9001/sa9001.go new file mode 100644 index 0000000000..1c97ccd55e --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa9001/sa9001.go @@ -0,0 +1,69 @@ +package sa9001 + +import ( + "go/ast" + "go/token" + "go/types" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/types/typeutil" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA9001", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Defers in range loops may not run when you expect them to`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + loop := node.(*ast.RangeStmt) + typ := pass.TypesInfo.TypeOf(loop.X) + _, ok := typeutil.CoreType(typ).(*types.Chan) + if !ok { + return + } + + stmts := []*ast.DeferStmt{} + exits := false + fn2 := func(node ast.Node) bool { + switch stmt := node.(type) { + case *ast.DeferStmt: + stmts = append(stmts, stmt) + case *ast.FuncLit: + // Don't look into function bodies + return false + case *ast.ReturnStmt: + exits = true + case *ast.BranchStmt: + exits = node.(*ast.BranchStmt).Tok == token.BREAK + } + return true + } + ast.Inspect(loop.Body, fn2) + + if exits { + return + } + for _, stmt := range stmts { + report.Report(pass, stmt, "defers in this range loop won't run unless the channel gets closed") + } + } + code.Preorder(pass, fn, (*ast.RangeStmt)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa9002/sa9002.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa9002/sa9002.go new file mode 100644 index 0000000000..054aa284a3 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa9002/sa9002.go @@ -0,0 +1,63 @@ +package sa9002 + +import ( + "fmt" + "go/ast" + "strconv" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/edit" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/types/typeutil" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA9002", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Using a non-octal \'os.FileMode\' that looks like it was meant to be in octal.`, + Since: "2017.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + call := node.(*ast.CallExpr) + for _, arg := range call.Args { + lit, ok := arg.(*ast.BasicLit) + if !ok { + continue + } + if !typeutil.IsTypeWithName(pass.TypesInfo.TypeOf(lit), "os.FileMode") && + !typeutil.IsTypeWithName(pass.TypesInfo.TypeOf(lit), "io/fs.FileMode") { + continue + } + if len(lit.Value) == 3 && + lit.Value[0] != '0' && + lit.Value[0] >= '0' && lit.Value[0] <= '7' && + lit.Value[1] >= '0' && lit.Value[1] <= '7' && + lit.Value[2] >= '0' && lit.Value[2] <= '7' { + + v, err := strconv.ParseInt(lit.Value, 10, 64) + if err != nil { + continue + } + report.Report(pass, arg, fmt.Sprintf("file mode '%s' evaluates to %#o; did you mean '0%s'?", lit.Value, v, lit.Value), + report.Fixes(edit.Fix("fix octal literal", edit.ReplaceWithString(arg, "0"+lit.Value)))) + } + } + } + code.Preorder(pass, fn, (*ast.CallExpr)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa9003/sa9003.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa9003/sa9003.go new file mode 100644 index 0000000000..9ee66475f1 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa9003/sa9003.go @@ -0,0 +1,62 @@ +package sa9003 + +import ( + "go/ast" + + "golang.org/x/tools/go/analysis" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/ir/irutil" + "honnef.co/go/tools/internal/passes/buildir" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA9003", + Run: run, + Requires: []*analysis.Analyzer{buildir.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Empty body in an if or else branch`, + Since: "2017.1", + NonDefault: true, + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { + if fn.Source() == nil { + continue + } + if irutil.IsExample(fn) { + continue + } + cb := func(node ast.Node) bool { + ifstmt, ok := node.(*ast.IfStmt) + if !ok { + return true + } + if ifstmt.Else != nil { + b, ok := ifstmt.Else.(*ast.BlockStmt) + if !ok || len(b.List) != 0 { + return true + } + report.Report(pass, ifstmt.Else, "empty branch", report.FilterGenerated(), report.ShortRange()) + } + if len(ifstmt.Body.List) != 0 { + return true + } + report.Report(pass, ifstmt, "empty branch", report.FilterGenerated(), report.ShortRange()) + return true + } + if source := fn.Source(); source != nil { + ast.Inspect(source, cb) + } + } + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa9004/sa9004.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa9004/sa9004.go new file mode 100644 index 0000000000..2e8838ee04 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa9004/sa9004.go @@ -0,0 +1,183 @@ +package sa9004 + +import ( + "go/ast" + "go/token" + "go/types" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/edit" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/ast/astutil" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA9004", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Only the first constant has an explicit type`, + + Text: `In a constant declaration such as the following: + + const ( + First byte = 1 + Second = 2 + ) + +the constant Second does not have the same type as the constant First. +This construct shouldn't be confused with + + const ( + First byte = iota + Second + ) + +where \'First\' and \'Second\' do indeed have the same type. The type is only +passed on when no explicit value is assigned to the constant. + +When declaring enumerations with explicit values it is therefore +important not to write + + const ( + EnumFirst EnumType = 1 + EnumSecond = 2 + EnumThird = 3 + ) + +This discrepancy in types can cause various confusing behaviors and +bugs. + + +Wrong type in variable declarations + +The most obvious issue with such incorrect enumerations expresses +itself as a compile error: + + package pkg + + const ( + EnumFirst uint8 = 1 + EnumSecond = 2 + ) + + func fn(useFirst bool) { + x := EnumSecond + if useFirst { + x = EnumFirst + } + } + +fails to compile with + + ./const.go:11:5: cannot use EnumFirst (type uint8) as type int in assignment + + +Losing method sets + +A more subtle issue occurs with types that have methods and optional +interfaces. Consider the following: + + package main + + import "fmt" + + type Enum int + + func (e Enum) String() string { + return "an enum" + } + + const ( + EnumFirst Enum = 1 + EnumSecond = 2 + ) + + func main() { + fmt.Println(EnumFirst) + fmt.Println(EnumSecond) + } + +This code will output + + an enum + 2 + +as \'EnumSecond\' has no explicit type, and thus defaults to \'int\'.`, + Since: "2019.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + decl := node.(*ast.GenDecl) + if !decl.Lparen.IsValid() { + return + } + if decl.Tok != token.CONST { + return + } + + groups := astutil.GroupSpecs(pass.Fset, decl.Specs) + groupLoop: + for _, group := range groups { + if len(group) < 2 { + continue + } + if group[0].(*ast.ValueSpec).Type == nil { + // first constant doesn't have a type + continue groupLoop + } + + firstType := pass.TypesInfo.TypeOf(group[0].(*ast.ValueSpec).Values[0]) + for i, spec := range group { + spec := spec.(*ast.ValueSpec) + if i > 0 && spec.Type != nil { + continue groupLoop + } + if len(spec.Names) != 1 || len(spec.Values) != 1 { + continue groupLoop + } + + if !types.ConvertibleTo(pass.TypesInfo.TypeOf(spec.Values[0]), firstType) { + continue groupLoop + } + + switch v := spec.Values[0].(type) { + case *ast.BasicLit: + case *ast.UnaryExpr: + if _, ok := v.X.(*ast.BasicLit); !ok { + continue groupLoop + } + default: + // if it's not a literal it might be typed, such as + // time.Microsecond = 1000 * Nanosecond + continue groupLoop + } + } + var edits []analysis.TextEdit + typ := group[0].(*ast.ValueSpec).Type + for _, spec := range group[1:] { + nspec := *spec.(*ast.ValueSpec) + nspec.Type = typ + // The position of `spec` node excludes comments (if any). + // However, on generating the source back from the node, the comments are included. Setting `Comment` to nil ensures deduplication of comments. + nspec.Comment = nil + edits = append(edits, edit.ReplaceWithNode(pass.Fset, spec, &nspec)) + } + report.Report(pass, group[0], "only the first constant in this group has an explicit type", report.Fixes(edit.Fix("add type to all constants in group", edits...))) + } + } + code.Preorder(pass, fn, (*ast.GenDecl)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa9005/sa9005.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa9005/sa9005.go new file mode 100644 index 0000000000..94c1f5dc9d --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa9005/sa9005.go @@ -0,0 +1,94 @@ +package sa9005 + +import ( + "fmt" + "go/types" + + "honnef.co/go/tools/analysis/callcheck" + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/go/types/typeutil" + "honnef.co/go/tools/internal/passes/buildir" + "honnef.co/go/tools/knowledge" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA9005", + Requires: []*analysis.Analyzer{ + buildir.Analyzer, + // Filtering generated code because it may include empty structs generated from data models. + generated.Analyzer, + }, + Run: callcheck.Analyzer(rules), + }, + Doc: &lint.RawDocumentation{ + Title: `Trying to marshal a struct with no public fields nor custom marshaling`, + Text: ` +The \'encoding/json\' and \'encoding/xml\' packages only operate on exported +fields in structs, not unexported ones. It is usually an error to try +to (un)marshal structs that only consist of unexported fields. + +This check will not flag calls involving types that define custom +marshaling behavior, e.g. via \'MarshalJSON\' methods. It will also not +flag empty structs.`, + Since: "2019.2", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAll, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var rules = map[string]callcheck.Check{ + // TODO(dh): should we really flag XML? Even an empty struct + // produces a non-zero amount of data, namely its type name. + // Let's see if we encounter any false positives. + // + // Also, should we flag gob? + "encoding/json.Marshal": check(knowledge.Arg("json.Marshal.v"), "MarshalJSON", "MarshalText"), + "encoding/xml.Marshal": check(knowledge.Arg("xml.Marshal.v"), "MarshalXML", "MarshalText"), + "(*encoding/json.Encoder).Encode": check(knowledge.Arg("(*encoding/json.Encoder).Encode.v"), "MarshalJSON", "MarshalText"), + "(*encoding/xml.Encoder).Encode": check(knowledge.Arg("(*encoding/xml.Encoder).Encode.v"), "MarshalXML", "MarshalText"), + + "encoding/json.Unmarshal": check(knowledge.Arg("json.Unmarshal.v"), "UnmarshalJSON", "UnmarshalText"), + "encoding/xml.Unmarshal": check(knowledge.Arg("xml.Unmarshal.v"), "UnmarshalXML", "UnmarshalText"), + "(*encoding/json.Decoder).Decode": check(knowledge.Arg("(*encoding/json.Decoder).Decode.v"), "UnmarshalJSON", "UnmarshalText"), + "(*encoding/xml.Decoder).Decode": check(knowledge.Arg("(*encoding/xml.Decoder).Decode.v"), "UnmarshalXML", "UnmarshalText"), +} + +func check(argN int, meths ...string) callcheck.Check { + return func(call *callcheck.Call) { + if code.IsGenerated(call.Pass, call.Instr.Pos()) { + return + } + arg := call.Args[argN] + T := arg.Value.Value.Type() + Ts, ok := typeutil.Dereference(T).Underlying().(*types.Struct) + if !ok { + return + } + if Ts.NumFields() == 0 { + return + } + fields := typeutil.FlattenFields(Ts) + for _, field := range fields { + if field.Var.Exported() { + return + } + } + // OPT(dh): we could use a method set cache here + ms := call.Instr.Parent().Prog.MethodSets.MethodSet(T) + // TODO(dh): we're not checking the signature, which can cause false negatives. + // This isn't a huge problem, however, since vet complains about incorrect signatures. + for _, meth := range meths { + if ms.Lookup(nil, meth) != nil { + return + } + } + arg.Invalid(fmt.Sprintf("struct type '%s' doesn't have any exported fields, nor custom marshaling", typeutil.Dereference(T))) + } +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa9006/sa9006.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa9006/sa9006.go new file mode 100644 index 0000000000..44f9923993 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa9006/sa9006.go @@ -0,0 +1,106 @@ +package sa9006 + +import ( + "fmt" + "go/ast" + "go/types" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA9006", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Dubious bit shifting of a fixed size integer value`, + Text: `Bit shifting a value past its size will always clear the value. + +For instance: + + v := int8(42) + v >>= 8 + +will always result in 0. + +This check flags bit shifting operations on fixed size integer values only. +That is, int, uint and uintptr are never flagged to avoid potential false +positives in somewhat exotic but valid bit twiddling tricks: + + // Clear any value above 32 bits if integers are more than 32 bits. + func f(i int) int { + v := i >> 32 + v = v << 32 + return i-v + }`, + Since: "2020.2", + Severity: lint.SeverityWarning, + // Technically this should be MergeIfAll, because the type of + // v might be different for different build tags. Practically, + // don't write code that depends on that. + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer +var ( + checkFixedLengthTypeShiftQ = pattern.MustParse(` + (Or + (AssignStmt _ (Or ">>=" "<<=") _) + (BinaryExpr _ (Or ">>" "<<") _)) + `) +) + +func run(pass *analysis.Pass) (interface{}, error) { + isDubiousShift := func(x, y ast.Expr) (int64, int64, bool) { + typ, ok := pass.TypesInfo.TypeOf(x).Underlying().(*types.Basic) + if !ok { + return 0, 0, false + } + switch typ.Kind() { + case types.Int8, types.Int16, types.Int32, types.Int64, + types.Uint8, types.Uint16, types.Uint32, types.Uint64: + // We're only interested in fixed–size types. + default: + return 0, 0, false + } + + const bitsInByte = 8 + typeBits := pass.TypesSizes.Sizeof(typ) * bitsInByte + + shiftLength, ok := code.ExprToInt(pass, y) + if !ok { + return 0, 0, false + } + + return typeBits, shiftLength, shiftLength >= typeBits + } + + fn := func(node ast.Node) { + if _, ok := code.Match(pass, checkFixedLengthTypeShiftQ, node); !ok { + return + } + + switch e := node.(type) { + case *ast.AssignStmt: + if size, shift, yes := isDubiousShift(e.Lhs[0], e.Rhs[0]); yes { + report.Report(pass, e, fmt.Sprintf("shifting %d-bit value by %d bits will always clear it", size, shift)) + } + case *ast.BinaryExpr: + if size, shift, yes := isDubiousShift(e.X, e.Y); yes { + report.Report(pass, e, fmt.Sprintf("shifting %d-bit value by %d bits will always clear it", size, shift)) + } + } + } + code.Preorder(pass, fn, (*ast.AssignStmt)(nil), (*ast.BinaryExpr)(nil)) + + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa9007/sa9007.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa9007/sa9007.go new file mode 100644 index 0000000000..d10e5e7e80 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa9007/sa9007.go @@ -0,0 +1,114 @@ +package sa9007 + +import ( + "fmt" + "os" + + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/ir" + "honnef.co/go/tools/go/ir/irutil" + "honnef.co/go/tools/internal/passes/buildir" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA9007", + Run: run, + Requires: []*analysis.Analyzer{buildir.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: "Deleting a directory that shouldn't be deleted", + Text: ` +It is virtually never correct to delete system directories such as +/tmp or the user's home directory. However, it can be fairly easy to +do by mistake, for example by mistakingly using \'os.TempDir\' instead +of \'ioutil.TempDir\', or by forgetting to add a suffix to the result +of \'os.UserHomeDir\'. + +Writing + + d := os.TempDir() + defer os.RemoveAll(d) + +in your unit tests will have a devastating effect on the stability of your system. + +This check flags attempts at deleting the following directories: + +- os.TempDir +- os.UserCacheDir +- os.UserConfigDir +- os.UserHomeDir +`, + Since: "2022.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { + for _, b := range fn.Blocks { + for _, instr := range b.Instrs { + call, ok := instr.(ir.CallInstruction) + if !ok { + continue + } + if !irutil.IsCallTo(call.Common(), "os.RemoveAll") { + continue + } + + kind := "" + ex := "" + callName := "" + arg := irutil.Flatten(call.Common().Args[0]) + switch arg := arg.(type) { + case *ir.Call: + callName = irutil.CallName(&arg.Call) + if callName != "os.TempDir" { + continue + } + kind = "temporary" + ex = os.TempDir() + case *ir.Extract: + if arg.Index != 0 { + continue + } + first, ok := arg.Tuple.(*ir.Call) + if !ok { + continue + } + callName = irutil.CallName(&first.Call) + switch callName { + case "os.UserCacheDir": + kind = "cache" + ex, _ = os.UserCacheDir() + case "os.UserConfigDir": + kind = "config" + ex, _ = os.UserConfigDir() + case "os.UserHomeDir": + kind = "home" + ex, _ = os.UserHomeDir() + default: + continue + } + default: + continue + } + + if ex == "" { + report.Report(pass, call, fmt.Sprintf("this call to os.RemoveAll deletes the user's entire %s directory, not a subdirectory therein", kind), + report.Related(arg, fmt.Sprintf("this call to %s returns the user's %s directory", callName, kind))) + } else { + report.Report(pass, call, fmt.Sprintf("this call to os.RemoveAll deletes the user's entire %s directory, not a subdirectory therein", kind), + report.Related(arg, fmt.Sprintf("this call to %s returns the user's %s directory, for example %s", callName, kind, ex))) + } + } + } + } + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa9008/sa9008.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa9008/sa9008.go new file mode 100644 index 0000000000..355b11e426 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa9008/sa9008.go @@ -0,0 +1,130 @@ +package sa9008 + +import ( + "fmt" + "go/ast" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/ast/astutil" + "honnef.co/go/tools/go/ir" + "honnef.co/go/tools/go/ir/irutil" + "honnef.co/go/tools/internal/passes/buildir" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA9008", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer, buildir.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `\'else\' branch of a type assertion is probably not reading the right value`, + Text: ` +When declaring variables as part of an \'if\' statement (like in \"if +foo := ...; foo {\"), the same variables will also be in the scope of +the \'else\' branch. This means that in the following example + + if x, ok := x.(int); ok { + // ... + } else { + fmt.Printf("unexpected type %T", x) + } + +\'x\' in the \'else\' branch will refer to the \'x\' from \'x, ok +:=\'; it will not refer to the \'x\' that is being type-asserted. The +result of a failed type assertion is the zero value of the type that +is being asserted to, so \'x\' in the else branch will always have the +value \'0\' and the type \'int\'. +`, + Since: "2022.1", + Severity: lint.SeverityWarning, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var typeAssertionShadowingElseQ = pattern.MustParse(`(IfStmt (AssignStmt [obj@(Ident _) ok@(Ident _)] ":=" assert@(TypeAssertExpr obj _)) ok _ elseBranch)`) + +func run(pass *analysis.Pass) (interface{}, error) { + // TODO(dh): without the IR-based verification, this check is able + // to find more bugs, but also more prone to false positives. It + // would be a good candidate for the 'codereview' category of + // checks. + + irpkg := pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg + fn := func(node ast.Node) { + m, ok := code.Match(pass, typeAssertionShadowingElseQ, node) + if !ok { + return + } + shadow := pass.TypesInfo.ObjectOf(m.State["obj"].(*ast.Ident)) + shadowed := m.State["assert"].(*ast.TypeAssertExpr).X + + path, exact := astutil.PathEnclosingInterval(code.File(pass, shadow), shadow.Pos(), shadow.Pos()) + if !exact { + // TODO(dh): when can this happen? + return + } + irfn := ir.EnclosingFunction(irpkg, path) + if irfn == nil { + // For example for functions named "_", because we don't generate IR for them. + return + } + + shadoweeIR, isAddr := irfn.ValueForExpr(m.State["obj"].(*ast.Ident)) + if shadoweeIR == nil || isAddr { + // TODO(dh): is this possible? + return + } + + var branch ast.Node + switch br := m.State["elseBranch"].(type) { + case ast.Node: + branch = br + case []ast.Stmt: + branch = &ast.BlockStmt{List: br} + case nil: + return + default: + panic(fmt.Sprintf("unexpected type %T", br)) + } + + ast.Inspect(branch, func(node ast.Node) bool { + ident, ok := node.(*ast.Ident) + if !ok { + return true + } + if pass.TypesInfo.ObjectOf(ident) != shadow { + return true + } + + v, isAddr := irfn.ValueForExpr(ident) + if v == nil || isAddr { + return true + } + if irutil.Flatten(v) != shadoweeIR { + // Same types.Object, but different IR value. This + // either means that the variable has been + // assigned to since the type assertion, or that + // the variable has escaped to the heap. Either + // way, we shouldn't flag reads of it. + return true + } + + report.Report(pass, ident, + fmt.Sprintf("%s refers to the result of a failed type assertion and is a zero value, not the value that was being type-asserted", report.Render(pass, ident)), + report.Related(shadow, "this is the variable being read"), + report.Related(shadowed, "this is the variable being shadowed")) + return true + }) + } + code.Preorder(pass, fn, (*ast.IfStmt)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/staticcheck/sa9009/sa9009.go b/tools/vendor/honnef.co/go/tools/staticcheck/sa9009/sa9009.go new file mode 100644 index 0000000000..f98db5aee9 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/staticcheck/sa9009/sa9009.go @@ -0,0 +1,68 @@ +package sa9009 + +import ( + "fmt" + "strings" + + "golang.org/x/tools/go/analysis" + + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "SA9009", + Run: run, + Requires: []*analysis.Analyzer{}, + }, + Doc: &lint.RawDocumentation{ + Title: "Ineffectual Go compiler directive", + Text: ` +A potential Go compiler directive was found, but is ineffectual as it begins +with whitespace.`, + Since: "2024.1", + Severity: lint.SeverityWarning, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (any, error) { + for _, f := range pass.Files { + for _, cg := range f.Comments { + for _, c := range cg.List { + // Compiler directives have to be // comments + if !strings.HasPrefix(c.Text, "//") { + continue + } + if pass.Fset.PositionFor(c.Pos(), false).Column != 1 { + // Compiler directives have to be top-level. This also + // avoids a false positive for + // 'import _ "unsafe" // go:linkname' + continue + } + text := strings.TrimLeft(c.Text[2:], " \t") + if len(text) == len(c.Text[2:]) { + // There was no leading whitespace + continue + } + if !strings.HasPrefix(text, "go:") { + // Not an attempted compiler directive + continue + } + text = text[3:] + if len(text) == 0 || text[0] < 'a' || text[0] > 'z' { + // A letter other than a-z after "go:", so unlikely to be an + // attempted compiler directive + continue + } + report.Report(pass, c, + fmt.Sprintf( + "ineffectual compiler directive due to extraneous space: %q", + c.Text)) + } + } + } + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/stylecheck/analysis.go b/tools/vendor/honnef.co/go/tools/stylecheck/analysis.go index d560d972ed..7171befe26 100644 --- a/tools/vendor/honnef.co/go/tools/stylecheck/analysis.go +++ b/tools/vendor/honnef.co/go/tools/stylecheck/analysis.go @@ -1,84 +1,46 @@ +// Code generated by generate.go. DO NOT EDIT. + package stylecheck import ( - "honnef.co/go/tools/analysis/facts/generated" - "honnef.co/go/tools/analysis/facts/tokenfile" "honnef.co/go/tools/analysis/lint" - "honnef.co/go/tools/config" - "honnef.co/go/tools/internal/passes/buildir" - "honnef.co/go/tools/internal/sharedcheck" - - "golang.org/x/tools/go/analysis" - "golang.org/x/tools/go/analysis/passes/inspect" + "honnef.co/go/tools/stylecheck/st1000" + "honnef.co/go/tools/stylecheck/st1001" + "honnef.co/go/tools/stylecheck/st1003" + "honnef.co/go/tools/stylecheck/st1005" + "honnef.co/go/tools/stylecheck/st1006" + "honnef.co/go/tools/stylecheck/st1008" + "honnef.co/go/tools/stylecheck/st1011" + "honnef.co/go/tools/stylecheck/st1012" + "honnef.co/go/tools/stylecheck/st1013" + "honnef.co/go/tools/stylecheck/st1015" + "honnef.co/go/tools/stylecheck/st1016" + "honnef.co/go/tools/stylecheck/st1017" + "honnef.co/go/tools/stylecheck/st1018" + "honnef.co/go/tools/stylecheck/st1019" + "honnef.co/go/tools/stylecheck/st1020" + "honnef.co/go/tools/stylecheck/st1021" + "honnef.co/go/tools/stylecheck/st1022" + "honnef.co/go/tools/stylecheck/st1023" ) -var Analyzers = lint.InitializeAnalyzers(Docs, map[string]*analysis.Analyzer{ - "ST1000": { - Run: CheckPackageComment, - }, - "ST1001": { - Run: CheckDotImports, - Requires: []*analysis.Analyzer{generated.Analyzer, config.Analyzer}, - }, - "ST1003": { - Run: CheckNames, - Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer, config.Analyzer}, - }, - "ST1005": { - Run: CheckErrorStrings, - Requires: []*analysis.Analyzer{buildir.Analyzer}, - }, - "ST1006": { - Run: CheckReceiverNames, - Requires: []*analysis.Analyzer{buildir.Analyzer, generated.Analyzer}, - }, - "ST1008": { - Run: CheckErrorReturn, - Requires: []*analysis.Analyzer{buildir.Analyzer}, - }, - "ST1011": { - Run: CheckTimeNames, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - "ST1012": { - Run: CheckErrorVarNames, - }, - "ST1013": { - Run: CheckHTTPStatusCodes, - // TODO(dh): why does this depend on tokenfile.TokenFile? - Requires: []*analysis.Analyzer{generated.Analyzer, tokenfile.Analyzer, config.Analyzer, inspect.Analyzer}, - }, - "ST1015": { - Run: CheckDefaultCaseOrder, - Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer, tokenfile.Analyzer}, - }, - "ST1016": { - Run: CheckReceiverNamesIdentical, - Requires: []*analysis.Analyzer{buildir.Analyzer, generated.Analyzer}, - }, - "ST1017": { - Run: CheckYodaConditions, - Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer, tokenfile.Analyzer}, - }, - "ST1018": { - Run: CheckInvisibleCharacters, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - }, - "ST1019": { - Run: CheckDuplicatedImports, - Requires: []*analysis.Analyzer{generated.Analyzer}, - }, - "ST1020": { - Run: CheckExportedFunctionDocs, - Requires: []*analysis.Analyzer{generated.Analyzer, inspect.Analyzer}, - }, - "ST1021": { - Run: CheckExportedTypeDocs, - Requires: []*analysis.Analyzer{generated.Analyzer, inspect.Analyzer}, - }, - "ST1022": { - Run: CheckExportedVarDocs, - Requires: []*analysis.Analyzer{generated.Analyzer, inspect.Analyzer}, - }, - "ST1023": sharedcheck.RedundantTypeInDeclarationChecker("should", false), -}) +var Analyzers = []*lint.Analyzer{ + st1000.SCAnalyzer, + st1001.SCAnalyzer, + st1003.SCAnalyzer, + st1005.SCAnalyzer, + st1006.SCAnalyzer, + st1008.SCAnalyzer, + st1011.SCAnalyzer, + st1012.SCAnalyzer, + st1013.SCAnalyzer, + st1015.SCAnalyzer, + st1016.SCAnalyzer, + st1017.SCAnalyzer, + st1018.SCAnalyzer, + st1019.SCAnalyzer, + st1020.SCAnalyzer, + st1021.SCAnalyzer, + st1022.SCAnalyzer, + st1023.SCAnalyzer, +} diff --git a/tools/vendor/honnef.co/go/tools/stylecheck/doc.go b/tools/vendor/honnef.co/go/tools/stylecheck/doc.go index 7ac707ee28..29649906a4 100644 --- a/tools/vendor/honnef.co/go/tools/stylecheck/doc.go +++ b/tools/vendor/honnef.co/go/tools/stylecheck/doc.go @@ -1,3 +1,5 @@ +//go:generate go run ../generate.go + // Package stylecheck contains analyzes that enforce style rules. // Most of the recommendations made are universally agreed upon by the wider Go community. // Some analyzes, however, implement stricter rules that not everyone will agree with. @@ -6,257 +8,3 @@ // For the most part it is recommended to follow the advice given by the analyzers that are enabled by default, // but you may want to disable additional analyzes on a case by case basis. package stylecheck - -import "honnef.co/go/tools/analysis/lint" - -var Docs = lint.Markdownify(map[string]*lint.RawDocumentation{ - "ST1000": { - Title: `Incorrect or missing package comment`, - Text: `Packages must have a package comment that is formatted according to -the guidelines laid out in -https://github.com/golang/go/wiki/CodeReviewComments#package-comments.`, - Since: "2019.1", - NonDefault: true, - MergeIf: lint.MergeIfAny, - }, - - "ST1001": { - Title: `Dot imports are discouraged`, - Text: `Dot imports that aren't in external test packages are discouraged. - -The \'dot_import_whitelist\' option can be used to whitelist certain -imports. - -Quoting Go Code Review Comments: - -> The \'import .\' form can be useful in tests that, due to circular -> dependencies, cannot be made part of the package being tested: -> -> package foo_test -> -> import ( -> "bar/testutil" // also imports "foo" -> . "foo" -> ) -> -> In this case, the test file cannot be in package foo because it -> uses \'bar/testutil\', which imports \'foo\'. So we use the \'import .\' -> form to let the file pretend to be part of package foo even though -> it is not. Except for this one case, do not use \'import .\' in your -> programs. It makes the programs much harder to read because it is -> unclear whether a name like \'Quux\' is a top-level identifier in the -> current package or in an imported package.`, - Since: "2019.1", - Options: []string{"dot_import_whitelist"}, - MergeIf: lint.MergeIfAny, - }, - - "ST1003": { - Title: `Poorly chosen identifier`, - Text: `Identifiers, such as variable and package names, follow certain rules. - -See the following links for details: - -- https://golang.org/doc/effective_go.html#package-names -- https://golang.org/doc/effective_go.html#mixed-caps -- https://github.com/golang/go/wiki/CodeReviewComments#initialisms -- https://github.com/golang/go/wiki/CodeReviewComments#variable-names`, - Since: "2019.1", - NonDefault: true, - Options: []string{"initialisms"}, - MergeIf: lint.MergeIfAny, - }, - - "ST1005": { - Title: `Incorrectly formatted error string`, - Text: `Error strings follow a set of guidelines to ensure uniformity and good -composability. - -Quoting Go Code Review Comments: - -> Error strings should not be capitalized (unless beginning with -> proper nouns or acronyms) or end with punctuation, since they are -> usually printed following other context. That is, use -> \'fmt.Errorf("something bad")\' not \'fmt.Errorf("Something bad")\', so -> that \'log.Printf("Reading %s: %v", filename, err)\' formats without a -> spurious capital letter mid-message.`, - Since: "2019.1", - MergeIf: lint.MergeIfAny, - }, - - "ST1006": { - Title: `Poorly chosen receiver name`, - Text: `Quoting Go Code Review Comments: - -> The name of a method's receiver should be a reflection of its -> identity; often a one or two letter abbreviation of its type -> suffices (such as "c" or "cl" for "Client"). Don't use generic -> names such as "me", "this" or "self", identifiers typical of -> object-oriented languages that place more emphasis on methods as -> opposed to functions. The name need not be as descriptive as that -> of a method argument, as its role is obvious and serves no -> documentary purpose. It can be very short as it will appear on -> almost every line of every method of the type; familiarity admits -> brevity. Be consistent, too: if you call the receiver "c" in one -> method, don't call it "cl" in another.`, - Since: "2019.1", - MergeIf: lint.MergeIfAny, - }, - - "ST1008": { - Title: `A function's error value should be its last return value`, - Text: `A function's error value should be its last return value.`, - Since: `2019.1`, - MergeIf: lint.MergeIfAny, - }, - - "ST1011": { - Title: `Poorly chosen name for variable of type \'time.Duration\'`, - Text: `\'time.Duration\' values represent an amount of time, which is represented -as a count of nanoseconds. An expression like \'5 * time.Microsecond\' -yields the value \'5000\'. It is therefore not appropriate to suffix a -variable of type \'time.Duration\' with any time unit, such as \'Msec\' or -\'Milli\'.`, - Since: `2019.1`, - MergeIf: lint.MergeIfAny, - }, - - "ST1012": { - Title: `Poorly chosen name for error variable`, - Text: `Error variables that are part of an API should be called \'errFoo\' or -\'ErrFoo\'.`, - Since: "2019.1", - MergeIf: lint.MergeIfAny, - }, - - "ST1013": { - Title: `Should use constants for HTTP error codes, not magic numbers`, - Text: `HTTP has a tremendous number of status codes. While some of those are -well known (200, 400, 404, 500), most of them are not. The \'net/http\' -package provides constants for all status codes that are part of the -various specifications. It is recommended to use these constants -instead of hard-coding magic numbers, to vastly improve the -readability of your code.`, - Since: "2019.1", - Options: []string{"http_status_code_whitelist"}, - MergeIf: lint.MergeIfAny, - }, - - "ST1015": { - Title: `A switch's default case should be the first or last case`, - Since: "2019.1", - MergeIf: lint.MergeIfAny, - }, - - "ST1016": { - Title: `Use consistent method receiver names`, - Since: "2019.1", - NonDefault: true, - MergeIf: lint.MergeIfAny, - }, - - "ST1017": { - Title: `Don't use Yoda conditions`, - Text: `Yoda conditions are conditions of the kind \"if 42 == x\", where the -literal is on the left side of the comparison. These are a common -idiom in languages in which assignment is an expression, to avoid bugs -of the kind \"if (x = 42)\". In Go, which doesn't allow for this kind of -bug, we prefer the more idiomatic \"if x == 42\".`, - Since: "2019.2", - MergeIf: lint.MergeIfAny, - }, - - "ST1018": { - Title: `Avoid zero-width and control characters in string literals`, - Since: "2019.2", - MergeIf: lint.MergeIfAny, - }, - - "ST1019": { - Title: `Importing the same package multiple times`, - Text: `Go allows importing the same package multiple times, as long as -different import aliases are being used. That is, the following -bit of code is valid: - - import ( - "fmt" - fumpt "fmt" - format "fmt" - _ "fmt" - ) - -However, this is very rarely done on purpose. Usually, it is a -sign of code that got refactored, accidentally adding duplicate -import statements. It is also a rarely known feature, which may -contribute to confusion. - -Do note that sometimes, this feature may be used -intentionally (see for example -https://github.com/golang/go/commit/3409ce39bfd7584523b7a8c150a310cea92d879d) -– if you want to allow this pattern in your code base, you're -advised to disable this check.`, - Since: "2020.1", - MergeIf: lint.MergeIfAny, - }, - - "ST1020": { - Title: "The documentation of an exported function should start with the function's name", - Text: `Doc comments work best as complete sentences, which -allow a wide variety of automated presentations. The first sentence -should be a one-sentence summary that starts with the name being -declared. - -If every doc comment begins with the name of the item it describes, -you can use the \'doc\' subcommand of the \'go\' tool and run the output -through grep. - -See https://golang.org/doc/effective_go.html#commentary for more -information on how to write good documentation.`, - Since: "2020.1", - NonDefault: true, - MergeIf: lint.MergeIfAny, - }, - - "ST1021": { - Title: "The documentation of an exported type should start with type's name", - Text: `Doc comments work best as complete sentences, which -allow a wide variety of automated presentations. The first sentence -should be a one-sentence summary that starts with the name being -declared. - -If every doc comment begins with the name of the item it describes, -you can use the \'doc\' subcommand of the \'go\' tool and run the output -through grep. - -See https://golang.org/doc/effective_go.html#commentary for more -information on how to write good documentation.`, - Since: "2020.1", - NonDefault: true, - MergeIf: lint.MergeIfAny, - }, - - "ST1022": { - Title: "The documentation of an exported variable or constant should start with variable's name", - Text: `Doc comments work best as complete sentences, which -allow a wide variety of automated presentations. The first sentence -should be a one-sentence summary that starts with the name being -declared. - -If every doc comment begins with the name of the item it describes, -you can use the \'doc\' subcommand of the \'go\' tool and run the output -through grep. - -See https://golang.org/doc/effective_go.html#commentary for more -information on how to write good documentation.`, - Since: "2020.1", - NonDefault: true, - MergeIf: lint.MergeIfAny, - }, - - "ST1023": { - Title: "Redundant type in variable declaration", - Since: "2021.1", - NonDefault: true, - MergeIf: lint.MergeIfAll, - }, -}) diff --git a/tools/vendor/honnef.co/go/tools/stylecheck/lint.go b/tools/vendor/honnef.co/go/tools/stylecheck/lint.go deleted file mode 100644 index a15e6d97ac..0000000000 --- a/tools/vendor/honnef.co/go/tools/stylecheck/lint.go +++ /dev/null @@ -1,990 +0,0 @@ -package stylecheck - -import ( - "fmt" - "go/ast" - "go/constant" - "go/token" - "go/types" - "sort" - "strconv" - "strings" - "unicode" - "unicode/utf8" - - "honnef.co/go/tools/analysis/code" - "honnef.co/go/tools/analysis/edit" - "honnef.co/go/tools/analysis/lint" - "honnef.co/go/tools/analysis/report" - "honnef.co/go/tools/config" - "honnef.co/go/tools/go/ast/astutil" - "honnef.co/go/tools/go/ir" - "honnef.co/go/tools/go/ir/irutil" - "honnef.co/go/tools/go/types/typeutil" - "honnef.co/go/tools/internal/passes/buildir" - "honnef.co/go/tools/pattern" - - "golang.org/x/tools/go/analysis" - "golang.org/x/tools/go/analysis/passes/inspect" - "golang.org/x/tools/go/ast/inspector" -) - -func docText(doc *ast.CommentGroup) (string, bool) { - if doc == nil { - return "", false - } - // We trim spaces primarily because of /**/ style comments, which often have leading space. - text := strings.TrimSpace(doc.Text()) - return text, text != "" -} - -func CheckPackageComment(pass *analysis.Pass) (interface{}, error) { - // - At least one file in a non-main package should have a package comment - // - // - The comment should be of the form - // "Package x ...". This has a slight potential for false - // positives, as multiple files can have package comments, in - // which case they get appended. But that doesn't happen a lot in - // the real world. - - if pass.Pkg.Name() == "main" { - return nil, nil - } - hasDocs := false - for _, f := range pass.Files { - if code.IsInTest(pass, f) { - continue - } - text, ok := docText(f.Doc) - if ok { - hasDocs = true - prefix := "Package " + f.Name.Name + " " - if !strings.HasPrefix(text, prefix) { - report.Report(pass, f.Doc, fmt.Sprintf(`package comment should be of the form "%s..."`, prefix)) - } - } - } - - if !hasDocs { - for _, f := range pass.Files { - if code.IsInTest(pass, f) { - continue - } - report.Report(pass, f, "at least one file in a package should have a package comment", report.ShortRange()) - } - } - return nil, nil -} - -func CheckDotImports(pass *analysis.Pass) (interface{}, error) { - for _, f := range pass.Files { - imports: - for _, imp := range f.Imports { - path := imp.Path.Value - path = path[1 : len(path)-1] - for _, w := range config.For(pass).DotImportWhitelist { - if w == path { - continue imports - } - } - - if imp.Name != nil && imp.Name.Name == "." && !code.IsInTest(pass, f) { - report.Report(pass, imp, "should not use dot imports", report.FilterGenerated()) - } - } - } - return nil, nil -} - -func CheckDuplicatedImports(pass *analysis.Pass) (interface{}, error) { - for _, f := range pass.Files { - // Collect all imports by their import path - imports := make(map[string][]*ast.ImportSpec, len(f.Imports)) - for _, imp := range f.Imports { - imports[imp.Path.Value] = append(imports[imp.Path.Value], imp) - } - - for path, value := range imports { - if path[1:len(path)-1] == "unsafe" { - // Don't flag unsafe. Cgo generated code imports - // unsafe using the blank identifier, and most - // user-written cgo code also imports unsafe - // explicitly. - continue - } - // If there's more than one import per path, we flag that - if len(value) > 1 { - s := fmt.Sprintf("package %s is being imported more than once", path) - opts := []report.Option{report.FilterGenerated()} - for _, imp := range value[1:] { - opts = append(opts, report.Related(imp, fmt.Sprintf("other import of %s", path))) - } - report.Report(pass, value[0], s, opts...) - } - } - } - return nil, nil -} - -func CheckBlankImports(pass *analysis.Pass) (interface{}, error) { - fset := pass.Fset - for _, f := range pass.Files { - if code.IsMainLike(pass) || code.IsInTest(pass, f) { - continue - } - - // Collect imports of the form `import _ "foo"`, i.e. with no - // parentheses, as their comment will be associated with the - // (paren-free) GenDecl, not the import spec itself. - // - // We don't directly process the GenDecl so that we can - // correctly handle the following: - // - // import _ "foo" - // import _ "bar" - // - // where only the first import should get flagged. - skip := map[ast.Spec]bool{} - ast.Inspect(f, func(node ast.Node) bool { - switch node := node.(type) { - case *ast.File: - return true - case *ast.GenDecl: - if node.Tok != token.IMPORT { - return false - } - if node.Lparen == token.NoPos && node.Doc != nil { - skip[node.Specs[0]] = true - } - return false - } - return false - }) - for i, imp := range f.Imports { - pos := fset.Position(imp.Pos()) - - if !astutil.IsBlank(imp.Name) { - continue - } - // Only flag the first blank import in a group of imports, - // or don't flag any of them, if the first one is - // commented - if i > 0 { - prev := f.Imports[i-1] - prevPos := fset.Position(prev.Pos()) - if pos.Line-1 == prevPos.Line && astutil.IsBlank(prev.Name) { - continue - } - } - - if imp.Doc == nil && imp.Comment == nil && !skip[imp] { - report.Report(pass, imp, "a blank import should be only in a main or test package, or have a comment justifying it") - } - } - } - return nil, nil -} - -var checkIncDecQ = pattern.MustParse(`(AssignStmt x tok@(Or "+=" "-=") (BasicLit "INT" "1"))`) - -func CheckIncDec(pass *analysis.Pass) (interface{}, error) { - // TODO(dh): this can be noisy for function bodies that look like this: - // x += 3 - // ... - // x += 2 - // ... - // x += 1 - fn := func(node ast.Node) { - m, ok := code.Match(pass, checkIncDecQ, node) - if !ok { - return - } - suffix := "" - switch m.State["tok"].(token.Token) { - case token.ADD_ASSIGN: - suffix = "++" - case token.SUB_ASSIGN: - suffix = "--" - } - - report.Report(pass, node, fmt.Sprintf("should replace %s with %s%s", report.Render(pass, node), report.Render(pass, m.State["x"].(ast.Node)), suffix)) - } - code.Preorder(pass, fn, (*ast.AssignStmt)(nil)) - return nil, nil -} - -func CheckErrorReturn(pass *analysis.Pass) (interface{}, error) { -fnLoop: - for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { - sig := fn.Type().(*types.Signature) - rets := sig.Results() - if rets == nil || rets.Len() < 2 { - continue - } - - if rets.At(rets.Len()-1).Type() == types.Universe.Lookup("error").Type() { - // Last return type is error. If the function also returns - // errors in other positions, that's fine. - continue - } - - if rets.Len() >= 2 && rets.At(rets.Len()-1).Type() == types.Universe.Lookup("bool").Type() && rets.At(rets.Len()-2).Type() == types.Universe.Lookup("error").Type() { - // Accept (..., error, bool) and assume it's a comma-ok function. It's not clear whether the bool should come last or not for these kinds of functions. - continue - } - for i := rets.Len() - 2; i >= 0; i-- { - if rets.At(i).Type() == types.Universe.Lookup("error").Type() { - report.Report(pass, rets.At(i), "error should be returned as the last argument", report.ShortRange()) - continue fnLoop - } - } - } - return nil, nil -} - -// CheckUnexportedReturn checks that exported functions on exported -// types do not return unexported types. -func CheckUnexportedReturn(pass *analysis.Pass) (interface{}, error) { - for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { - if fn.Synthetic != 0 || fn.Parent() != nil { - continue - } - if !ast.IsExported(fn.Name()) || code.IsMain(pass) || code.IsInTest(pass, fn) { - continue - } - sig := fn.Type().(*types.Signature) - if sig.Recv() != nil && !ast.IsExported(typeutil.Dereference(sig.Recv().Type()).(*types.Named).Obj().Name()) { - continue - } - res := sig.Results() - for i := 0; i < res.Len(); i++ { - if named, ok := typeutil.DereferenceR(res.At(i).Type()).(*types.Named); ok && - !ast.IsExported(named.Obj().Name()) && - named != types.Universe.Lookup("error").Type() { - report.Report(pass, fn, "should not return unexported type") - } - } - } - return nil, nil -} - -func CheckReceiverNames(pass *analysis.Pass) (interface{}, error) { - irpkg := pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg - for _, m := range irpkg.Members { - if T, ok := m.Object().(*types.TypeName); ok && !T.IsAlias() { - ms := typeutil.IntuitiveMethodSet(T.Type(), nil) - for _, sel := range ms { - fn := sel.Obj().(*types.Func) - recv := fn.Type().(*types.Signature).Recv() - if typeutil.Dereference(recv.Type()) != T.Type() { - // skip embedded methods - continue - } - if recv.Name() == "self" || recv.Name() == "this" { - report.Report(pass, recv, `receiver name should be a reflection of its identity; don't use generic names such as "this" or "self"`, report.FilterGenerated()) - } - if recv.Name() == "_" { - report.Report(pass, recv, "receiver name should not be an underscore, omit the name if it is unused", report.FilterGenerated()) - } - } - } - } - return nil, nil -} - -func CheckReceiverNamesIdentical(pass *analysis.Pass) (interface{}, error) { - irpkg := pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg - for _, m := range irpkg.Members { - names := map[string]int{} - - var firstFn *types.Func - if T, ok := m.Object().(*types.TypeName); ok && !T.IsAlias() { - ms := typeutil.IntuitiveMethodSet(T.Type(), nil) - for _, sel := range ms { - fn := sel.Obj().(*types.Func) - recv := fn.Type().(*types.Signature).Recv() - if code.IsGenerated(pass, recv.Pos()) { - // Don't concern ourselves with methods in generated code - continue - } - if typeutil.Dereference(recv.Type()) != T.Type() { - // skip embedded methods - continue - } - if firstFn == nil { - firstFn = fn - } - if recv.Name() != "" && recv.Name() != "_" { - names[recv.Name()]++ - } - } - } - - if len(names) > 1 { - var seen []string - for name, count := range names { - seen = append(seen, fmt.Sprintf("%dx %q", count, name)) - } - sort.Strings(seen) - - report.Report(pass, firstFn, fmt.Sprintf("methods on the same type should have the same receiver name (seen %s)", strings.Join(seen, ", "))) - } - } - return nil, nil -} - -func CheckContextFirstArg(pass *analysis.Pass) (interface{}, error) { - // TODO(dh): this check doesn't apply to test helpers. Example from the stdlib: - // func helperCommandContext(t *testing.T, ctx context.Context, s ...string) (cmd *exec.Cmd) { -fnLoop: - for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { - if fn.Synthetic != 0 || fn.Parent() != nil { - continue - } - params := fn.Signature.Params() - if params.Len() < 2 { - continue - } - if types.TypeString(params.At(0).Type(), nil) == "context.Context" { - continue - } - for i := 1; i < params.Len(); i++ { - param := params.At(i) - if types.TypeString(param.Type(), nil) == "context.Context" { - report.Report(pass, param, "context.Context should be the first argument of a function", report.ShortRange()) - continue fnLoop - } - } - } - return nil, nil -} - -func CheckErrorStrings(pass *analysis.Pass) (interface{}, error) { - objNames := map[*ir.Package]map[string]bool{} - irpkg := pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg - objNames[irpkg] = map[string]bool{} - for _, m := range irpkg.Members { - if typ, ok := m.(*ir.Type); ok { - objNames[irpkg][typ.Name()] = true - } - } - for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { - objNames[fn.Package()][fn.Name()] = true - } - - for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { - if code.IsInTest(pass, fn) { - // We don't care about malformed error messages in tests; - // they're usually for direct human consumption, not part - // of an API - continue - } - for _, block := range fn.Blocks { - instrLoop: - for _, ins := range block.Instrs { - call, ok := ins.(*ir.Call) - if !ok { - continue - } - if !irutil.IsCallToAny(call.Common(), "errors.New", "fmt.Errorf") { - continue - } - - k, ok := call.Common().Args[0].(*ir.Const) - if !ok { - continue - } - - s := constant.StringVal(k.Value) - if len(s) == 0 { - continue - } - switch s[len(s)-1] { - case '.', ':', '!', '\n': - report.Report(pass, call, "error strings should not end with punctuation or newlines") - } - idx := strings.IndexByte(s, ' ') - if idx == -1 { - // single word error message, probably not a real - // error but something used in tests or during - // debugging - continue - } - word := s[:idx] - first, n := utf8.DecodeRuneInString(word) - if !unicode.IsUpper(first) { - continue - } - for _, c := range word[n:] { - if unicode.IsUpper(c) || unicode.IsDigit(c) { - // Word is probably an initialism or multi-word function name. Digits cover elliptic curves like - // P384. - continue instrLoop - } - } - - if strings.ContainsRune(word, '(') { - // Might be a function call - continue instrLoop - } - word = strings.TrimRightFunc(word, func(r rune) bool { return unicode.IsPunct(r) }) - if objNames[fn.Package()][word] { - // Word is probably the name of a function or type in this package - continue - } - // First word in error starts with a capital - // letter, and the word doesn't contain any other - // capitals, making it unlikely to be an - // initialism or multi-word function name. - // - // It could still be a proper noun, though. - - report.Report(pass, call, "error strings should not be capitalized") - } - } - } - return nil, nil -} - -func CheckTimeNames(pass *analysis.Pass) (interface{}, error) { - suffixes := []string{ - "Sec", "Secs", "Seconds", - "Msec", "Msecs", - "Milli", "Millis", "Milliseconds", - "Usec", "Usecs", "Microseconds", - "MS", "Ms", - } - fn := func(names []*ast.Ident) { - for _, name := range names { - if _, ok := pass.TypesInfo.Defs[name]; !ok { - continue - } - T := pass.TypesInfo.TypeOf(name) - if !typeutil.IsType(T, "time.Duration") && !typeutil.IsType(T, "*time.Duration") { - continue - } - for _, suffix := range suffixes { - if strings.HasSuffix(name.Name, suffix) { - report.Report(pass, name, fmt.Sprintf("var %s is of type %v; don't use unit-specific suffix %q", name.Name, T, suffix)) - break - } - } - } - } - - fn2 := func(node ast.Node) { - switch node := node.(type) { - case *ast.ValueSpec: - fn(node.Names) - case *ast.FieldList: - for _, field := range node.List { - fn(field.Names) - } - case *ast.AssignStmt: - if node.Tok != token.DEFINE { - break - } - var names []*ast.Ident - for _, lhs := range node.Lhs { - if lhs, ok := lhs.(*ast.Ident); ok { - names = append(names, lhs) - } - } - fn(names) - } - } - - code.Preorder(pass, fn2, (*ast.ValueSpec)(nil), (*ast.FieldList)(nil), (*ast.AssignStmt)(nil)) - return nil, nil -} - -func CheckErrorVarNames(pass *analysis.Pass) (interface{}, error) { - for _, f := range pass.Files { - for _, decl := range f.Decls { - gen, ok := decl.(*ast.GenDecl) - if !ok || gen.Tok != token.VAR { - continue - } - for _, spec := range gen.Specs { - spec := spec.(*ast.ValueSpec) - if len(spec.Names) != len(spec.Values) { - continue - } - - for i, name := range spec.Names { - val := spec.Values[i] - if !code.IsCallToAny(pass, val, "errors.New", "fmt.Errorf") { - continue - } - - if pass.Pkg.Path() == "net/http" && strings.HasPrefix(name.Name, "http2err") { - // special case for internal variable names of - // bundled HTTP 2 code in net/http - continue - } - prefix := "err" - if name.IsExported() { - prefix = "Err" - } - if !strings.HasPrefix(name.Name, prefix) { - report.Report(pass, name, fmt.Sprintf("error var %s should have name of the form %sFoo", name.Name, prefix)) - } - } - } - } - } - return nil, nil -} - -var httpStatusCodes = map[int64]string{ - 100: "StatusContinue", - 101: "StatusSwitchingProtocols", - 102: "StatusProcessing", - 200: "StatusOK", - 201: "StatusCreated", - 202: "StatusAccepted", - 203: "StatusNonAuthoritativeInfo", - 204: "StatusNoContent", - 205: "StatusResetContent", - 206: "StatusPartialContent", - 207: "StatusMultiStatus", - 208: "StatusAlreadyReported", - 226: "StatusIMUsed", - 300: "StatusMultipleChoices", - 301: "StatusMovedPermanently", - 302: "StatusFound", - 303: "StatusSeeOther", - 304: "StatusNotModified", - 305: "StatusUseProxy", - 307: "StatusTemporaryRedirect", - 308: "StatusPermanentRedirect", - 400: "StatusBadRequest", - 401: "StatusUnauthorized", - 402: "StatusPaymentRequired", - 403: "StatusForbidden", - 404: "StatusNotFound", - 405: "StatusMethodNotAllowed", - 406: "StatusNotAcceptable", - 407: "StatusProxyAuthRequired", - 408: "StatusRequestTimeout", - 409: "StatusConflict", - 410: "StatusGone", - 411: "StatusLengthRequired", - 412: "StatusPreconditionFailed", - 413: "StatusRequestEntityTooLarge", - 414: "StatusRequestURITooLong", - 415: "StatusUnsupportedMediaType", - 416: "StatusRequestedRangeNotSatisfiable", - 417: "StatusExpectationFailed", - 418: "StatusTeapot", - 422: "StatusUnprocessableEntity", - 423: "StatusLocked", - 424: "StatusFailedDependency", - 426: "StatusUpgradeRequired", - 428: "StatusPreconditionRequired", - 429: "StatusTooManyRequests", - 431: "StatusRequestHeaderFieldsTooLarge", - 451: "StatusUnavailableForLegalReasons", - 500: "StatusInternalServerError", - 501: "StatusNotImplemented", - 502: "StatusBadGateway", - 503: "StatusServiceUnavailable", - 504: "StatusGatewayTimeout", - 505: "StatusHTTPVersionNotSupported", - 506: "StatusVariantAlsoNegotiates", - 507: "StatusInsufficientStorage", - 508: "StatusLoopDetected", - 510: "StatusNotExtended", - 511: "StatusNetworkAuthenticationRequired", -} - -func CheckHTTPStatusCodes(pass *analysis.Pass) (interface{}, error) { - whitelist := map[string]bool{} - for _, code := range config.For(pass).HTTPStatusCodeWhitelist { - whitelist[code] = true - } - fn := func(node ast.Node) { - call := node.(*ast.CallExpr) - - var arg int - switch code.CallName(pass, call) { - case "net/http.Error": - arg = 2 - case "net/http.Redirect": - arg = 3 - case "net/http.StatusText": - arg = 0 - case "net/http.RedirectHandler": - arg = 1 - default: - return - } - if arg >= len(call.Args) { - return - } - tv, ok := code.IntegerLiteral(pass, call.Args[arg]) - if !ok { - return - } - n, ok := constant.Int64Val(tv.Value) - if !ok { - return - } - if whitelist[strconv.FormatInt(n, 10)] { - return - } - - s, ok := httpStatusCodes[n] - if !ok { - return - } - lit := call.Args[arg] - report.Report(pass, lit, fmt.Sprintf("should use constant http.%s instead of numeric literal %d", s, n), - report.FilterGenerated(), - report.Fixes(edit.Fix(fmt.Sprintf("use http.%s instead of %d", s, n), edit.ReplaceWithString(lit, "http."+s)))) - } - code.Preorder(pass, fn, (*ast.CallExpr)(nil)) - return nil, nil -} - -func CheckDefaultCaseOrder(pass *analysis.Pass) (interface{}, error) { - hasFallthrough := func(clause ast.Stmt) bool { - // A valid fallthrough statement may be used only as the final non-empty statement in a case clause. Thus we can - // easily avoid falsely matching fallthroughs in nested switches by not descending into blocks. - - body := clause.(*ast.CaseClause).Body - for i := len(body) - 1; i >= 0; i-- { - last := body[i] - switch stmt := last.(type) { - case *ast.EmptyStmt: - // Fallthrough may be followed by empty statements - case *ast.BranchStmt: - return stmt.Tok == token.FALLTHROUGH - default: - return false - } - } - - return false - } - - fn := func(node ast.Node) { - stmt := node.(*ast.SwitchStmt) - list := stmt.Body.List - defaultIdx := -1 - for i, c := range list { - if c.(*ast.CaseClause).List == nil { - defaultIdx = i - break - } - } - - if defaultIdx == -1 || defaultIdx == 0 || defaultIdx == len(list)-1 { - // No default case, or it's the first or last case - return - } - - if hasFallthrough(list[defaultIdx-1]) || hasFallthrough(list[defaultIdx]) { - // We either fall into or out of this case; don't mess with the order - return - } - - report.Report(pass, list[defaultIdx], "default case should be first or last in switch statement", report.FilterGenerated()) - } - code.Preorder(pass, fn, (*ast.SwitchStmt)(nil)) - return nil, nil -} - -var ( - checkYodaConditionsQ = pattern.MustParse(`(BinaryExpr left@(TrulyConstantExpression _) tok@(Or "==" "!=") right@(Not (TrulyConstantExpression _)))`) - checkYodaConditionsR = pattern.MustParse(`(BinaryExpr right tok left)`) -) - -func CheckYodaConditions(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - if _, edits, ok := code.MatchAndEdit(pass, checkYodaConditionsQ, checkYodaConditionsR, node); ok { - report.Report(pass, node, "don't use Yoda conditions", - report.FilterGenerated(), - report.Fixes(edit.Fix("un-Yoda-fy", edits...))) - } - } - code.Preorder(pass, fn, (*ast.BinaryExpr)(nil)) - return nil, nil -} - -func CheckInvisibleCharacters(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - lit := node.(*ast.BasicLit) - if lit.Kind != token.STRING { - return - } - - type invalid struct { - r rune - off int - } - var invalids []invalid - hasFormat := false - hasControl := false - prev := rune(-1) - const zwj = '\u200d' - for off, r := range lit.Value { - if unicode.Is(unicode.Cf, r) { - // Don't flag joined emojis. These are multiple emojis joined with ZWJ, which some platform render as single composite emojis. - // For the purpose of this check, we consider all symbols, including all symbol modifiers, emoji. - if r != zwj || (r == zwj && !unicode.Is(unicode.S, prev)) { - invalids = append(invalids, invalid{r, off}) - hasFormat = true - } - } else if unicode.Is(unicode.Cc, r) && r != '\n' && r != '\t' && r != '\r' { - invalids = append(invalids, invalid{r, off}) - hasControl = true - } - prev = r - } - - switch len(invalids) { - case 0: - return - case 1: - var kind string - if hasFormat { - kind = "format" - } else if hasControl { - kind = "control" - } else { - panic("unreachable") - } - - r := invalids[0] - msg := fmt.Sprintf("string literal contains the Unicode %s character %U, consider using the %q escape sequence instead", kind, r.r, r.r) - - replacement := strconv.QuoteRune(r.r) - replacement = replacement[1 : len(replacement)-1] - edit := analysis.SuggestedFix{ - Message: fmt.Sprintf("replace %s character %U with %q", kind, r.r, r.r), - TextEdits: []analysis.TextEdit{{ - Pos: lit.Pos() + token.Pos(r.off), - End: lit.Pos() + token.Pos(r.off) + token.Pos(utf8.RuneLen(r.r)), - NewText: []byte(replacement), - }}, - } - delete := analysis.SuggestedFix{ - Message: fmt.Sprintf("delete %s character %U", kind, r.r), - TextEdits: []analysis.TextEdit{{ - Pos: lit.Pos() + token.Pos(r.off), - End: lit.Pos() + token.Pos(r.off) + token.Pos(utf8.RuneLen(r.r)), - }}, - } - report.Report(pass, lit, msg, report.Fixes(edit, delete)) - default: - var kind string - if hasFormat && hasControl { - kind = "format and control" - } else if hasFormat { - kind = "format" - } else if hasControl { - kind = "control" - } else { - panic("unreachable") - } - - msg := fmt.Sprintf("string literal contains Unicode %s characters, consider using escape sequences instead", kind) - var edits []analysis.TextEdit - var deletions []analysis.TextEdit - for _, r := range invalids { - replacement := strconv.QuoteRune(r.r) - replacement = replacement[1 : len(replacement)-1] - edits = append(edits, analysis.TextEdit{ - Pos: lit.Pos() + token.Pos(r.off), - End: lit.Pos() + token.Pos(r.off) + token.Pos(utf8.RuneLen(r.r)), - NewText: []byte(replacement), - }) - deletions = append(deletions, analysis.TextEdit{ - Pos: lit.Pos() + token.Pos(r.off), - End: lit.Pos() + token.Pos(r.off) + token.Pos(utf8.RuneLen(r.r)), - }) - } - edit := analysis.SuggestedFix{ - Message: fmt.Sprintf("replace all %s characters with escape sequences", kind), - TextEdits: edits, - } - delete := analysis.SuggestedFix{ - Message: fmt.Sprintf("delete all %s characters", kind), - TextEdits: deletions, - } - report.Report(pass, lit, msg, report.Fixes(edit, delete)) - } - } - code.Preorder(pass, fn, (*ast.BasicLit)(nil)) - return nil, nil -} - -func CheckExportedFunctionDocs(pass *analysis.Pass) (interface{}, error) { - fn := func(node ast.Node) { - if code.IsInTest(pass, node) { - return - } - - decl := node.(*ast.FuncDecl) - text, ok := docText(decl.Doc) - if !ok { - return - } - if !ast.IsExported(decl.Name.Name) { - return - } - kind := "function" - if decl.Recv != nil { - kind = "method" - var ident *ast.Ident - T := decl.Recv.List[0].Type - if T_, ok := T.(*ast.StarExpr); ok { - T = T_.X - } - switch T := T.(type) { - case *ast.IndexExpr: - ident = T.X.(*ast.Ident) - case *ast.IndexListExpr: - ident = T.X.(*ast.Ident) - case *ast.Ident: - ident = T - default: - lint.ExhaustiveTypeSwitch(T) - } - if !ast.IsExported(ident.Name) { - return - } - } - prefix := decl.Name.Name + " " - if !strings.HasPrefix(text, prefix) { - report.Report(pass, decl.Doc, fmt.Sprintf(`comment on exported %s %s should be of the form "%s..."`, kind, decl.Name.Name, prefix), report.FilterGenerated()) - } - } - - code.Preorder(pass, fn, (*ast.FuncDecl)(nil)) - return nil, nil -} - -func CheckExportedTypeDocs(pass *analysis.Pass) (interface{}, error) { - var genDecl *ast.GenDecl - fn := func(node ast.Node, push bool) bool { - if !push { - genDecl = nil - return false - } - if code.IsInTest(pass, node) { - return false - } - - switch node := node.(type) { - case *ast.GenDecl: - if node.Tok == token.IMPORT { - return false - } - genDecl = node - return true - case *ast.TypeSpec: - if !ast.IsExported(node.Name.Name) { - return false - } - - doc := node.Doc - text, ok := docText(doc) - if !ok { - if len(genDecl.Specs) != 1 { - // more than one spec in the GenDecl, don't validate the - // docstring - return false - } - if genDecl.Lparen.IsValid() { - // 'type ( T )' is weird, don't guess the user's intention - return false - } - doc = genDecl.Doc - text, ok = docText(doc) - if !ok { - return false - } - } - - // Check comment before we strip articles in case the type's name is an article. - if strings.HasPrefix(text, node.Name.Name+" ") { - return false - } - - s := text - articles := [...]string{"A", "An", "The"} - for _, a := range articles { - if strings.HasPrefix(s, a+" ") { - s = s[len(a)+1:] - break - } - } - if !strings.HasPrefix(s, node.Name.Name+" ") { - report.Report(pass, doc, fmt.Sprintf(`comment on exported type %s should be of the form "%s ..." (with optional leading article)`, node.Name.Name, node.Name.Name), report.FilterGenerated()) - } - return false - case *ast.FuncLit, *ast.FuncDecl: - return false - default: - lint.ExhaustiveTypeSwitch(node) - return false - } - } - - pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Nodes([]ast.Node{(*ast.GenDecl)(nil), (*ast.TypeSpec)(nil), (*ast.FuncLit)(nil), (*ast.FuncDecl)(nil)}, fn) - return nil, nil -} - -func CheckExportedVarDocs(pass *analysis.Pass) (interface{}, error) { - var genDecl *ast.GenDecl - fn := func(node ast.Node, push bool) bool { - if !push { - genDecl = nil - return false - } - if code.IsInTest(pass, node) { - return false - } - - switch node := node.(type) { - case *ast.GenDecl: - if node.Tok == token.IMPORT { - return false - } - genDecl = node - return true - case *ast.ValueSpec: - if genDecl.Lparen.IsValid() || len(node.Names) > 1 { - // Don't try to guess the user's intention - return false - } - name := node.Names[0].Name - if !ast.IsExported(name) { - return false - } - text, ok := docText(genDecl.Doc) - if !ok { - return false - } - prefix := name + " " - if !strings.HasPrefix(text, prefix) { - kind := "var" - if genDecl.Tok == token.CONST { - kind = "const" - } - report.Report(pass, genDecl.Doc, fmt.Sprintf(`comment on exported %s %s should be of the form "%s..."`, kind, name, prefix), report.FilterGenerated()) - } - return false - case *ast.FuncLit, *ast.FuncDecl: - return false - default: - lint.ExhaustiveTypeSwitch(node) - return false - } - } - - pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Nodes([]ast.Node{(*ast.GenDecl)(nil), (*ast.ValueSpec)(nil), (*ast.FuncLit)(nil), (*ast.FuncDecl)(nil)}, fn) - return nil, nil -} diff --git a/tools/vendor/honnef.co/go/tools/stylecheck/st1000/st1000.go b/tools/vendor/honnef.co/go/tools/stylecheck/st1000/st1000.go new file mode 100644 index 0000000000..246dbc247d --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/stylecheck/st1000/st1000.go @@ -0,0 +1,83 @@ +package st1000 + +import ( + "fmt" + "go/ast" + "strings" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "ST1000", + Run: run, + }, + Doc: &lint.RawDocumentation{ + Title: `Incorrect or missing package comment`, + Text: `Packages must have a package comment that is formatted according to +the guidelines laid out in +https://go.dev/wiki/CodeReviewComments#package-comments.`, + Since: "2019.1", + NonDefault: true, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + // - At least one file in a non-main package should have a package comment + // + // - The comment should be of the form + // "Package x ...". This has a slight potential for false + // positives, as multiple files can have package comments, in + // which case they get appended. But that doesn't happen a lot in + // the real world. + + if pass.Pkg.Name() == "main" { + return nil, nil + } + hasDocs := false + for _, f := range pass.Files { + if code.IsInTest(pass, f) { + continue + } + text, ok := docText(f.Doc) + if ok { + hasDocs = true + prefix := "Package " + f.Name.Name + isNonAlpha := func(b byte) bool { + // This check only considers ASCII, which can lead to false negatives for some malformed package + // comments. + return !((b >= '0' && b <= '9') || (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z')) + } + if !strings.HasPrefix(text, prefix) || (len(text) > len(prefix) && !isNonAlpha(text[len(prefix)])) { + report.Report(pass, f.Doc, fmt.Sprintf(`package comment should be of the form "%s..."`, prefix)) + } + } + } + + if !hasDocs { + for _, f := range pass.Files { + if code.IsInTest(pass, f) { + continue + } + report.Report(pass, f, "at least one file in a package should have a package comment", report.ShortRange()) + } + } + return nil, nil +} + +func docText(doc *ast.CommentGroup) (string, bool) { + if doc == nil { + return "", false + } + // We trim spaces primarily because of /**/ style comments, which often have leading space. + text := strings.TrimSpace(doc.Text()) + return text, text != "" +} diff --git a/tools/vendor/honnef.co/go/tools/stylecheck/st1001/st1001.go b/tools/vendor/honnef.co/go/tools/stylecheck/st1001/st1001.go new file mode 100644 index 0000000000..3b2fd2ad28 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/stylecheck/st1001/st1001.go @@ -0,0 +1,71 @@ +package st1001 + +import ( + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/config" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "ST1001", + Run: run, + Requires: []*analysis.Analyzer{generated.Analyzer, config.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Dot imports are discouraged`, + Text: `Dot imports that aren't in external test packages are discouraged. + +The \'dot_import_whitelist\' option can be used to whitelist certain +imports. + +Quoting Go Code Review Comments: + +> The \'import .\' form can be useful in tests that, due to circular +> dependencies, cannot be made part of the package being tested: +> +> package foo_test +> +> import ( +> "bar/testutil" // also imports "foo" +> . "foo" +> ) +> +> In this case, the test file cannot be in package foo because it +> uses \'bar/testutil\', which imports \'foo\'. So we use the \'import .\' +> form to let the file pretend to be part of package foo even though +> it is not. Except for this one case, do not use \'import .\' in your +> programs. It makes the programs much harder to read because it is +> unclear whether a name like \'Quux\' is a top-level identifier in the +> current package or in an imported package.`, + Since: "2019.1", + Options: []string{"dot_import_whitelist"}, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + for _, f := range pass.Files { + imports: + for _, imp := range f.Imports { + path := imp.Path.Value + path = path[1 : len(path)-1] + for _, w := range config.For(pass).DotImportWhitelist { + if w == path { + continue imports + } + } + + if imp.Name != nil && imp.Name.Name == "." && !code.IsInTest(pass, f) { + report.Report(pass, imp, "should not use dot imports", report.FilterGenerated()) + } + } + } + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/stylecheck/names.go b/tools/vendor/honnef.co/go/tools/stylecheck/st1003/st1003.go similarity index 88% rename from tools/vendor/honnef.co/go/tools/stylecheck/names.go rename to tools/vendor/honnef.co/go/tools/stylecheck/st1003/st1003.go index d37bf6baf6..d2ff5a9e50 100644 --- a/tools/vendor/honnef.co/go/tools/stylecheck/names.go +++ b/tools/vendor/honnef.co/go/tools/stylecheck/st1003/st1003.go @@ -1,7 +1,4 @@ -// Copyright (c) 2013 The Go Authors. All rights reserved. -// Copyright (c) 2018 Dominik Honnef. All rights reserved. - -package stylecheck +package st1003 import ( "fmt" @@ -11,12 +8,40 @@ import ( "unicode" "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" "honnef.co/go/tools/analysis/report" "honnef.co/go/tools/config" "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" ) +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "ST1003", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer, config.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Poorly chosen identifier`, + Text: `Identifiers, such as variable and package names, follow certain rules. + +See the following links for details: + +- https://go.dev/doc/effective_go#package-names +- https://go.dev/doc/effective_go#mixed-caps +- https://go.dev/wiki/CodeReviewComments#initialisms +- https://go.dev/wiki/CodeReviewComments#variable-names`, + Since: "2019.1", + NonDefault: true, + Options: []string{"initialisms"}, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + // knownNameExceptions is a set of names that are known to be exempt from naming checks. // This is usually because they are constrained by having to match names in the // standard library. @@ -25,7 +50,7 @@ var knownNameExceptions = map[string]bool{ "kWh": true, } -func CheckNames(pass *analysis.Pass) (interface{}, error) { +func run(pass *analysis.Pass) (interface{}, error) { // A large part of this function is copied from // github.com/golang/lint, Copyright (c) 2013 The Go Authors, // licensed under the BSD 3-clause license. diff --git a/tools/vendor/honnef.co/go/tools/stylecheck/st1005/st1005.go b/tools/vendor/honnef.co/go/tools/stylecheck/st1005/st1005.go new file mode 100644 index 0000000000..a2909c3774 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/stylecheck/st1005/st1005.go @@ -0,0 +1,130 @@ +package st1005 + +import ( + "go/constant" + "strings" + "unicode" + "unicode/utf8" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/ir" + "honnef.co/go/tools/go/ir/irutil" + "honnef.co/go/tools/internal/passes/buildir" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "ST1005", + Run: run, + Requires: []*analysis.Analyzer{buildir.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Incorrectly formatted error string`, + Text: `Error strings follow a set of guidelines to ensure uniformity and good +composability. + +Quoting Go Code Review Comments: + +> Error strings should not be capitalized (unless beginning with +> proper nouns or acronyms) or end with punctuation, since they are +> usually printed following other context. That is, use +> \'fmt.Errorf("something bad")\' not \'fmt.Errorf("Something bad")\', so +> that \'log.Printf("Reading %s: %v", filename, err)\' formats without a +> spurious capital letter mid-message.`, + Since: "2019.1", + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + objNames := map[*ir.Package]map[string]bool{} + irpkg := pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg + objNames[irpkg] = map[string]bool{} + for _, m := range irpkg.Members { + if typ, ok := m.(*ir.Type); ok { + objNames[irpkg][typ.Name()] = true + } + } + for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { + objNames[fn.Package()][fn.Name()] = true + } + + for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { + if code.IsInTest(pass, fn) { + // We don't care about malformed error messages in tests; + // they're usually for direct human consumption, not part + // of an API + continue + } + for _, block := range fn.Blocks { + instrLoop: + for _, ins := range block.Instrs { + call, ok := ins.(*ir.Call) + if !ok { + continue + } + if !irutil.IsCallToAny(call.Common(), "errors.New", "fmt.Errorf") { + continue + } + + k, ok := call.Common().Args[0].(*ir.Const) + if !ok { + continue + } + + s := constant.StringVal(k.Value) + if len(s) == 0 { + continue + } + switch s[len(s)-1] { + case '.', ':', '!', '\n': + report.Report(pass, call, "error strings should not end with punctuation or newlines") + } + idx := strings.IndexByte(s, ' ') + if idx == -1 { + // single word error message, probably not a real + // error but something used in tests or during + // debugging + continue + } + word := s[:idx] + first, n := utf8.DecodeRuneInString(word) + if !unicode.IsUpper(first) { + continue + } + for _, c := range word[n:] { + if unicode.IsUpper(c) || unicode.IsDigit(c) { + // Word is probably an initialism or multi-word function name. Digits cover elliptic curves like + // P384. + continue instrLoop + } + } + + if strings.ContainsRune(word, '(') { + // Might be a function call + continue instrLoop + } + word = strings.TrimRightFunc(word, func(r rune) bool { return unicode.IsPunct(r) }) + if objNames[fn.Package()][word] { + // Word is probably the name of a function or type in this package + continue + } + // First word in error starts with a capital + // letter, and the word doesn't contain any other + // capitals, making it unlikely to be an + // initialism or multi-word function name. + // + // It could still be a proper noun, though. + + report.Report(pass, call, "error strings should not be capitalized") + } + } + } + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/stylecheck/st1006/st1006.go b/tools/vendor/honnef.co/go/tools/stylecheck/st1006/st1006.go new file mode 100644 index 0000000000..42aad45cd0 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/stylecheck/st1006/st1006.go @@ -0,0 +1,65 @@ +package st1006 + +import ( + "go/types" + + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/types/typeutil" + "honnef.co/go/tools/internal/passes/buildir" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "ST1006", + Run: run, + Requires: []*analysis.Analyzer{buildir.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Poorly chosen receiver name`, + Text: `Quoting Go Code Review Comments: + +> The name of a method's receiver should be a reflection of its +> identity; often a one or two letter abbreviation of its type +> suffices (such as "c" or "cl" for "Client"). Don't use generic +> names such as "me", "this" or "self", identifiers typical of +> object-oriented languages that place more emphasis on methods as +> opposed to functions. The name need not be as descriptive as that +> of a method argument, as its role is obvious and serves no +> documentary purpose. It can be very short as it will appear on +> almost every line of every method of the type; familiarity admits +> brevity. Be consistent, too: if you call the receiver "c" in one +> method, don't call it "cl" in another.`, + Since: "2019.1", + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + irpkg := pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg + for _, m := range irpkg.Members { + if T, ok := m.Object().(*types.TypeName); ok && !T.IsAlias() { + ms := typeutil.IntuitiveMethodSet(T.Type(), nil) + for _, sel := range ms { + fn := sel.Obj().(*types.Func) + recv := fn.Type().(*types.Signature).Recv() + if typeutil.Dereference(recv.Type()) != T.Type() { + // skip embedded methods + continue + } + if recv.Name() == "self" || recv.Name() == "this" { + report.Report(pass, recv, `receiver name should be a reflection of its identity; don't use generic names such as "this" or "self"`, report.FilterGenerated()) + } + if recv.Name() == "_" { + report.Report(pass, recv, "receiver name should not be an underscore, omit the name if it is unused", report.FilterGenerated()) + } + } + } + } + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/stylecheck/st1008/st1008.go b/tools/vendor/honnef.co/go/tools/stylecheck/st1008/st1008.go new file mode 100644 index 0000000000..23c557ea9b --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/stylecheck/st1008/st1008.go @@ -0,0 +1,58 @@ +package st1008 + +import ( + "go/types" + + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/internal/passes/buildir" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "ST1008", + Run: run, + Requires: []*analysis.Analyzer{buildir.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `A function's error value should be its last return value`, + Text: `A function's error value should be its last return value.`, + Since: `2019.1`, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { +fnLoop: + for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { + sig := fn.Type().(*types.Signature) + rets := sig.Results() + if rets == nil || rets.Len() < 2 { + continue + } + + if types.Unalias(rets.At(rets.Len()-1).Type()) == types.Universe.Lookup("error").Type() { + // Last return type is error. If the function also returns + // errors in other positions, that's fine. + continue + } + + if rets.Len() >= 2 && + types.Unalias(rets.At(rets.Len()-1).Type()) == types.Universe.Lookup("bool").Type() && + types.Unalias(rets.At(rets.Len()-2).Type()) == types.Universe.Lookup("error").Type() { + // Accept (..., error, bool) and assume it's a comma-ok function. It's not clear whether the bool should come last or not for these kinds of functions. + continue + } + for i := rets.Len() - 2; i >= 0; i-- { + if types.Unalias(rets.At(i).Type()) == types.Universe.Lookup("error").Type() { + report.Report(pass, rets.At(i), "error should be returned as the last argument", report.ShortRange()) + continue fnLoop + } + } + } + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/stylecheck/st1011/st1011.go b/tools/vendor/honnef.co/go/tools/stylecheck/st1011/st1011.go new file mode 100644 index 0000000000..3bf7ecdf12 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/stylecheck/st1011/st1011.go @@ -0,0 +1,88 @@ +package st1011 + +import ( + "fmt" + "go/ast" + "go/token" + "strings" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/types/typeutil" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "ST1011", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Poorly chosen name for variable of type \'time.Duration\'`, + Text: `\'time.Duration\' values represent an amount of time, which is represented +as a count of nanoseconds. An expression like \'5 * time.Microsecond\' +yields the value \'5000\'. It is therefore not appropriate to suffix a +variable of type \'time.Duration\' with any time unit, such as \'Msec\' or +\'Milli\'.`, + Since: `2019.1`, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + suffixes := []string{ + "Sec", "Secs", "Seconds", + "Msec", "Msecs", + "Milli", "Millis", "Milliseconds", + "Usec", "Usecs", "Microseconds", + "MS", "Ms", + } + fn := func(names []*ast.Ident) { + for _, name := range names { + if _, ok := pass.TypesInfo.Defs[name]; !ok { + continue + } + T := pass.TypesInfo.TypeOf(name) + if !typeutil.IsTypeWithName(T, "time.Duration") && !typeutil.IsPointerToTypeWithName(T, "time.Duration") { + continue + } + for _, suffix := range suffixes { + if strings.HasSuffix(name.Name, suffix) { + report.Report(pass, name, fmt.Sprintf("var %s is of type %v; don't use unit-specific suffix %q", name.Name, T, suffix)) + break + } + } + } + } + + fn2 := func(node ast.Node) { + switch node := node.(type) { + case *ast.ValueSpec: + fn(node.Names) + case *ast.FieldList: + for _, field := range node.List { + fn(field.Names) + } + case *ast.AssignStmt: + if node.Tok != token.DEFINE { + break + } + var names []*ast.Ident + for _, lhs := range node.Lhs { + if lhs, ok := lhs.(*ast.Ident); ok { + names = append(names, lhs) + } + } + fn(names) + } + } + + code.Preorder(pass, fn2, (*ast.ValueSpec)(nil), (*ast.FieldList)(nil), (*ast.AssignStmt)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/stylecheck/st1012/st1012.go b/tools/vendor/honnef.co/go/tools/stylecheck/st1012/st1012.go new file mode 100644 index 0000000000..fdeaee76d6 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/stylecheck/st1012/st1012.go @@ -0,0 +1,68 @@ +package st1012 + +import ( + "fmt" + "go/ast" + "go/token" + "strings" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "ST1012", + Run: run, + }, + Doc: &lint.RawDocumentation{ + Title: `Poorly chosen name for error variable`, + Text: `Error variables that are part of an API should be called \'errFoo\' or +\'ErrFoo\'.`, + Since: "2019.1", + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + for _, f := range pass.Files { + for _, decl := range f.Decls { + gen, ok := decl.(*ast.GenDecl) + if !ok || gen.Tok != token.VAR { + continue + } + for _, spec := range gen.Specs { + spec := spec.(*ast.ValueSpec) + if len(spec.Names) != len(spec.Values) { + continue + } + + for i, name := range spec.Names { + val := spec.Values[i] + if !code.IsCallToAny(pass, val, "errors.New", "fmt.Errorf") { + continue + } + + if pass.Pkg.Path() == "net/http" && strings.HasPrefix(name.Name, "http2err") { + // special case for internal variable names of + // bundled HTTP 2 code in net/http + continue + } + prefix := "err" + if name.IsExported() { + prefix = "Err" + } + if !strings.HasPrefix(name.Name, prefix) { + report.Report(pass, name, fmt.Sprintf("error var %s should have name of the form %sFoo", name.Name, prefix)) + } + } + } + } + } + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/stylecheck/st1013/st1013.go b/tools/vendor/honnef.co/go/tools/stylecheck/st1013/st1013.go new file mode 100644 index 0000000000..d28daa35e6 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/stylecheck/st1013/st1013.go @@ -0,0 +1,151 @@ +package st1013 + +import ( + "fmt" + "go/ast" + "go/constant" + "strconv" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/edit" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/config" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "ST1013", + Run: run, + Requires: []*analysis.Analyzer{generated.Analyzer, config.Analyzer, inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Should use constants for HTTP error codes, not magic numbers`, + Text: `HTTP has a tremendous number of status codes. While some of those are +well known (200, 400, 404, 500), most of them are not. The \'net/http\' +package provides constants for all status codes that are part of the +various specifications. It is recommended to use these constants +instead of hard-coding magic numbers, to vastly improve the +readability of your code.`, + Since: "2019.1", + Options: []string{"http_status_code_whitelist"}, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + whitelist := map[string]bool{} + for _, code := range config.For(pass).HTTPStatusCodeWhitelist { + whitelist[code] = true + } + fn := func(node ast.Node) { + call := node.(*ast.CallExpr) + + var arg int + switch code.CallName(pass, call) { + case "net/http.Error": + arg = 2 + case "net/http.Redirect": + arg = 3 + case "net/http.StatusText": + arg = 0 + case "net/http.RedirectHandler": + arg = 1 + default: + return + } + if arg >= len(call.Args) { + return + } + tv, ok := code.IntegerLiteral(pass, call.Args[arg]) + if !ok { + return + } + n, ok := constant.Int64Val(tv.Value) + if !ok { + return + } + if whitelist[strconv.FormatInt(n, 10)] { + return + } + + s, ok := httpStatusCodes[n] + if !ok { + return + } + lit := call.Args[arg] + report.Report(pass, lit, fmt.Sprintf("should use constant http.%s instead of numeric literal %d", s, n), + report.FilterGenerated(), + report.Fixes(edit.Fix(fmt.Sprintf("use http.%s instead of %d", s, n), edit.ReplaceWithString(lit, "http."+s)))) + } + code.Preorder(pass, fn, (*ast.CallExpr)(nil)) + return nil, nil +} + +var httpStatusCodes = map[int64]string{ + 100: "StatusContinue", + 101: "StatusSwitchingProtocols", + 102: "StatusProcessing", + 200: "StatusOK", + 201: "StatusCreated", + 202: "StatusAccepted", + 203: "StatusNonAuthoritativeInfo", + 204: "StatusNoContent", + 205: "StatusResetContent", + 206: "StatusPartialContent", + 207: "StatusMultiStatus", + 208: "StatusAlreadyReported", + 226: "StatusIMUsed", + 300: "StatusMultipleChoices", + 301: "StatusMovedPermanently", + 302: "StatusFound", + 303: "StatusSeeOther", + 304: "StatusNotModified", + 305: "StatusUseProxy", + 307: "StatusTemporaryRedirect", + 308: "StatusPermanentRedirect", + 400: "StatusBadRequest", + 401: "StatusUnauthorized", + 402: "StatusPaymentRequired", + 403: "StatusForbidden", + 404: "StatusNotFound", + 405: "StatusMethodNotAllowed", + 406: "StatusNotAcceptable", + 407: "StatusProxyAuthRequired", + 408: "StatusRequestTimeout", + 409: "StatusConflict", + 410: "StatusGone", + 411: "StatusLengthRequired", + 412: "StatusPreconditionFailed", + 413: "StatusRequestEntityTooLarge", + 414: "StatusRequestURITooLong", + 415: "StatusUnsupportedMediaType", + 416: "StatusRequestedRangeNotSatisfiable", + 417: "StatusExpectationFailed", + 418: "StatusTeapot", + 422: "StatusUnprocessableEntity", + 423: "StatusLocked", + 424: "StatusFailedDependency", + 426: "StatusUpgradeRequired", + 428: "StatusPreconditionRequired", + 429: "StatusTooManyRequests", + 431: "StatusRequestHeaderFieldsTooLarge", + 451: "StatusUnavailableForLegalReasons", + 500: "StatusInternalServerError", + 501: "StatusNotImplemented", + 502: "StatusBadGateway", + 503: "StatusServiceUnavailable", + 504: "StatusGatewayTimeout", + 505: "StatusHTTPVersionNotSupported", + 506: "StatusVariantAlsoNegotiates", + 507: "StatusInsufficientStorage", + 508: "StatusLoopDetected", + 510: "StatusNotExtended", + 511: "StatusNetworkAuthenticationRequired", +} diff --git a/tools/vendor/honnef.co/go/tools/stylecheck/st1015/st1015.go b/tools/vendor/honnef.co/go/tools/stylecheck/st1015/st1015.go new file mode 100644 index 0000000000..a03be7471d --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/stylecheck/st1015/st1015.go @@ -0,0 +1,77 @@ +package st1015 + +import ( + "go/ast" + "go/token" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "ST1015", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `A switch's default case should be the first or last case`, + Since: "2019.1", + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + hasFallthrough := func(clause ast.Stmt) bool { + // A valid fallthrough statement may be used only as the final non-empty statement in a case clause. Thus we can + // easily avoid falsely matching fallthroughs in nested switches by not descending into blocks. + + body := clause.(*ast.CaseClause).Body + for i := len(body) - 1; i >= 0; i-- { + last := body[i] + switch stmt := last.(type) { + case *ast.EmptyStmt: + // Fallthrough may be followed by empty statements + case *ast.BranchStmt: + return stmt.Tok == token.FALLTHROUGH + default: + return false + } + } + + return false + } + + fn := func(node ast.Node) { + stmt := node.(*ast.SwitchStmt) + list := stmt.Body.List + defaultIdx := -1 + for i, c := range list { + if c.(*ast.CaseClause).List == nil { + defaultIdx = i + break + } + } + + if defaultIdx == -1 || defaultIdx == 0 || defaultIdx == len(list)-1 { + // No default case, or it's the first or last case + return + } + + if hasFallthrough(list[defaultIdx-1]) || hasFallthrough(list[defaultIdx]) { + // We either fall into or out of this case; don't mess with the order + return + } + + report.Report(pass, list[defaultIdx], "default case should be first or last in switch statement", report.FilterGenerated()) + } + code.Preorder(pass, fn, (*ast.SwitchStmt)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/stylecheck/st1016/st1016.go b/tools/vendor/honnef.co/go/tools/stylecheck/st1016/st1016.go new file mode 100644 index 0000000000..d175a749a8 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/stylecheck/st1016/st1016.go @@ -0,0 +1,74 @@ +package st1016 + +import ( + "fmt" + "go/types" + "sort" + "strings" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/go/types/typeutil" + "honnef.co/go/tools/internal/passes/buildir" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "ST1016", + Run: run, + Requires: []*analysis.Analyzer{buildir.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Use consistent method receiver names`, + Since: "2019.1", + NonDefault: true, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + irpkg := pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg + for _, m := range irpkg.Members { + names := map[string]int{} + + var firstFn *types.Func + if T, ok := m.Object().(*types.TypeName); ok && !T.IsAlias() { + ms := typeutil.IntuitiveMethodSet(T.Type(), nil) + for _, sel := range ms { + fn := sel.Obj().(*types.Func) + recv := fn.Type().(*types.Signature).Recv() + if code.IsGenerated(pass, recv.Pos()) { + // Don't concern ourselves with methods in generated code + continue + } + if typeutil.Dereference(recv.Type()) != T.Type() { + // skip embedded methods + continue + } + if firstFn == nil { + firstFn = fn + } + if recv.Name() != "" && recv.Name() != "_" { + names[recv.Name()]++ + } + } + } + + if len(names) > 1 { + var seen []string + for name, count := range names { + seen = append(seen, fmt.Sprintf("%dx %q", count, name)) + } + sort.Strings(seen) + + report.Report(pass, firstFn, fmt.Sprintf("methods on the same type should have the same receiver name (seen %s)", strings.Join(seen, ", "))) + } + } + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/stylecheck/st1017/st1017.go b/tools/vendor/honnef.co/go/tools/stylecheck/st1017/st1017.go new file mode 100644 index 0000000000..6e8ccca64b --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/stylecheck/st1017/st1017.go @@ -0,0 +1,52 @@ +package st1017 + +import ( + "go/ast" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/edit" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + "honnef.co/go/tools/pattern" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "ST1017", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Don't use Yoda conditions`, + Text: `Yoda conditions are conditions of the kind \"if 42 == x\", where the +literal is on the left side of the comparison. These are a common +idiom in languages in which assignment is an expression, to avoid bugs +of the kind \"if (x = 42)\". In Go, which doesn't allow for this kind of +bug, we prefer the more idiomatic \"if x == 42\".`, + Since: "2019.2", + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +var ( + checkYodaConditionsQ = pattern.MustParse(`(BinaryExpr left@(TrulyConstantExpression _) tok@(Or "==" "!=") right@(Not (TrulyConstantExpression _)))`) + checkYodaConditionsR = pattern.MustParse(`(BinaryExpr right tok left)`) +) + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + if _, edits, ok := code.MatchAndEdit(pass, checkYodaConditionsQ, checkYodaConditionsR, node); ok { + report.Report(pass, node, "don't use Yoda conditions", + report.FilterGenerated(), + report.Fixes(edit.Fix("un-Yoda-fy", edits...))) + } + } + code.Preorder(pass, fn, (*ast.BinaryExpr)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/stylecheck/st1018/st1018.go b/tools/vendor/honnef.co/go/tools/stylecheck/st1018/st1018.go new file mode 100644 index 0000000000..85d961c619 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/stylecheck/st1018/st1018.go @@ -0,0 +1,157 @@ +package st1018 + +import ( + "fmt" + "go/ast" + "go/token" + "strconv" + "unicode" + "unicode/utf8" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "ST1018", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Avoid zero-width and control characters in string literals`, + Since: "2019.2", + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + lit := node.(*ast.BasicLit) + if lit.Kind != token.STRING { + return + } + + type invalid struct { + r rune + off int + } + var invalids []invalid + hasFormat := false + hasControl := false + prev := rune(-1) + const zwj = '\u200d' + for off, r := range lit.Value { + if unicode.Is(unicode.Cf, r) { + if r >= '\U000e0020' && r <= '\U000e007f' { + // These are used for spelling out country codes for flag emoji + } else if unicode.Is(unicode.Variation_Selector, r) { + // Always allow variation selectors + } else if r == zwj && (unicode.Is(unicode.S, prev) || unicode.Is(unicode.Variation_Selector, prev)) { + // Allow zero-width joiner in emoji, including those that use variation selectors. + + // Technically some foreign scripts make valid use of zero-width joiners, too, but for now we'll err + // on the side of flagging all non-emoji uses of ZWJ. + } else { + switch r { + case '\u0600', '\u0601', '\u0602', '\u0603', '\u0604', '\u0605', '\u0890', '\u0891', '\u08e2': + // Arabic characters that are not actually invisible. If anyone knows why these are in the + // Other, Format category please let me know. + case '\u061c', '\u202A', '\u202B', '\u202D', '\u202E', '\u2066', '\u2067', '\u2068', '\u202C', '\u2069': + // Bidirectional formatting characters. At best they will render confusingly, at worst they're used + // to cause confusion. + fallthrough + default: + invalids = append(invalids, invalid{r, off}) + hasFormat = true + } + } + } else if unicode.Is(unicode.Cc, r) && r != '\n' && r != '\t' && r != '\r' { + invalids = append(invalids, invalid{r, off}) + hasControl = true + } + prev = r + } + + switch len(invalids) { + case 0: + return + case 1: + var kind string + if hasFormat { + kind = "format" + } else if hasControl { + kind = "control" + } else { + panic("unreachable") + } + + r := invalids[0] + msg := fmt.Sprintf("string literal contains the Unicode %s character %U, consider using the %q escape sequence instead", kind, r.r, r.r) + + replacement := strconv.QuoteRune(r.r) + replacement = replacement[1 : len(replacement)-1] + edit := analysis.SuggestedFix{ + Message: fmt.Sprintf("replace %s character %U with %q", kind, r.r, r.r), + TextEdits: []analysis.TextEdit{{ + Pos: lit.Pos() + token.Pos(r.off), + End: lit.Pos() + token.Pos(r.off) + token.Pos(utf8.RuneLen(r.r)), + NewText: []byte(replacement), + }}, + } + delete := analysis.SuggestedFix{ + Message: fmt.Sprintf("delete %s character %U", kind, r.r), + TextEdits: []analysis.TextEdit{{ + Pos: lit.Pos() + token.Pos(r.off), + End: lit.Pos() + token.Pos(r.off) + token.Pos(utf8.RuneLen(r.r)), + }}, + } + report.Report(pass, lit, msg, report.Fixes(edit, delete)) + default: + var kind string + if hasFormat && hasControl { + kind = "format and control" + } else if hasFormat { + kind = "format" + } else if hasControl { + kind = "control" + } else { + panic("unreachable") + } + + msg := fmt.Sprintf("string literal contains Unicode %s characters, consider using escape sequences instead", kind) + var edits []analysis.TextEdit + var deletions []analysis.TextEdit + for _, r := range invalids { + replacement := strconv.QuoteRune(r.r) + replacement = replacement[1 : len(replacement)-1] + edits = append(edits, analysis.TextEdit{ + Pos: lit.Pos() + token.Pos(r.off), + End: lit.Pos() + token.Pos(r.off) + token.Pos(utf8.RuneLen(r.r)), + NewText: []byte(replacement), + }) + deletions = append(deletions, analysis.TextEdit{ + Pos: lit.Pos() + token.Pos(r.off), + End: lit.Pos() + token.Pos(r.off) + token.Pos(utf8.RuneLen(r.r)), + }) + } + edit := analysis.SuggestedFix{ + Message: fmt.Sprintf("replace all %s characters with escape sequences", kind), + TextEdits: edits, + } + delete := analysis.SuggestedFix{ + Message: fmt.Sprintf("delete all %s characters", kind), + TextEdits: deletions, + } + report.Report(pass, lit, msg, report.Fixes(edit, delete)) + } + } + code.Preorder(pass, fn, (*ast.BasicLit)(nil)) + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/stylecheck/st1019/st1019.go b/tools/vendor/honnef.co/go/tools/stylecheck/st1019/st1019.go new file mode 100644 index 0000000000..f08781ec34 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/stylecheck/st1019/st1019.go @@ -0,0 +1,78 @@ +package st1019 + +import ( + "fmt" + "go/ast" + + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + + "golang.org/x/tools/go/analysis" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "ST1019", + Run: run, + Requires: []*analysis.Analyzer{generated.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: `Importing the same package multiple times`, + Text: `Go allows importing the same package multiple times, as long as +different import aliases are being used. That is, the following +bit of code is valid: + + import ( + "fmt" + fumpt "fmt" + format "fmt" + _ "fmt" + ) + +However, this is very rarely done on purpose. Usually, it is a +sign of code that got refactored, accidentally adding duplicate +import statements. It is also a rarely known feature, which may +contribute to confusion. + +Do note that sometimes, this feature may be used +intentionally (see for example +https://github.com/golang/go/commit/3409ce39bfd7584523b7a8c150a310cea92d879d) +– if you want to allow this pattern in your code base, you're +advised to disable this check.`, + Since: "2020.1", + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + for _, f := range pass.Files { + // Collect all imports by their import path + imports := make(map[string][]*ast.ImportSpec, len(f.Imports)) + for _, imp := range f.Imports { + imports[imp.Path.Value] = append(imports[imp.Path.Value], imp) + } + + for path, value := range imports { + if path[1:len(path)-1] == "unsafe" { + // Don't flag unsafe. Cgo generated code imports + // unsafe using the blank identifier, and most + // user-written cgo code also imports unsafe + // explicitly. + continue + } + // If there's more than one import per path, we flag that + if len(value) > 1 { + s := fmt.Sprintf("package %s is being imported more than once", path) + opts := []report.Option{report.FilterGenerated()} + for _, imp := range value[1:] { + opts = append(opts, report.Related(imp, fmt.Sprintf("other import of %s", path))) + } + report.Report(pass, value[0], s, opts...) + } + } + } + return nil, nil +} diff --git a/tools/vendor/honnef.co/go/tools/stylecheck/st1020/st1020.go b/tools/vendor/honnef.co/go/tools/stylecheck/st1020/st1020.go new file mode 100644 index 0000000000..aed4290b69 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/stylecheck/st1020/st1020.go @@ -0,0 +1,101 @@ +package st1020 + +import ( + "fmt" + "go/ast" + "strings" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "ST1020", + Run: run, + Requires: []*analysis.Analyzer{generated.Analyzer, inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: "The documentation of an exported function should start with the function's name", + Text: `Doc comments work best as complete sentences, which +allow a wide variety of automated presentations. The first sentence +should be a one-sentence summary that starts with the name being +declared. + +If every doc comment begins with the name of the item it describes, +you can use the \'doc\' subcommand of the \'go\' tool and run the output +through grep. + +See https://go.dev/doc/effective_go#commentary for more +information on how to write good documentation.`, + Since: "2020.1", + NonDefault: true, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + fn := func(node ast.Node) { + if code.IsInTest(pass, node) { + return + } + + decl := node.(*ast.FuncDecl) + text, ok := docText(decl.Doc) + if !ok { + return + } + if !ast.IsExported(decl.Name.Name) { + return + } + if strings.HasPrefix(text, "Deprecated: ") { + return + } + + kind := "function" + if decl.Recv != nil { + kind = "method" + var ident *ast.Ident + T := decl.Recv.List[0].Type + if T_, ok := T.(*ast.StarExpr); ok { + T = T_.X + } + switch T := T.(type) { + case *ast.IndexExpr: + ident = T.X.(*ast.Ident) + case *ast.IndexListExpr: + ident = T.X.(*ast.Ident) + case *ast.Ident: + ident = T + default: + lint.ExhaustiveTypeSwitch(T) + } + if !ast.IsExported(ident.Name) { + return + } + } + prefix := decl.Name.Name + " " + if !strings.HasPrefix(text, prefix) { + report.Report(pass, decl.Doc, fmt.Sprintf(`comment on exported %s %s should be of the form "%s..."`, kind, decl.Name.Name, prefix), report.FilterGenerated()) + } + } + + code.Preorder(pass, fn, (*ast.FuncDecl)(nil)) + return nil, nil +} + +func docText(doc *ast.CommentGroup) (string, bool) { + if doc == nil { + return "", false + } + // We trim spaces primarily because of /**/ style comments, which often have leading space. + text := strings.TrimSpace(doc.Text()) + return text, text != "" +} diff --git a/tools/vendor/honnef.co/go/tools/stylecheck/st1021/st1021.go b/tools/vendor/honnef.co/go/tools/stylecheck/st1021/st1021.go new file mode 100644 index 0000000000..8ba6a5b233 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/stylecheck/st1021/st1021.go @@ -0,0 +1,124 @@ +package st1021 + +import ( + "fmt" + "go/ast" + "go/token" + "strings" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/inspector" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "ST1021", + Run: run, + Requires: []*analysis.Analyzer{generated.Analyzer, inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: "The documentation of an exported type should start with type's name", + Text: `Doc comments work best as complete sentences, which +allow a wide variety of automated presentations. The first sentence +should be a one-sentence summary that starts with the name being +declared. + +If every doc comment begins with the name of the item it describes, +you can use the \'doc\' subcommand of the \'go\' tool and run the output +through grep. + +See https://go.dev/doc/effective_go#commentary for more +information on how to write good documentation.`, + Since: "2020.1", + NonDefault: true, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + var genDecl *ast.GenDecl + fn := func(node ast.Node, push bool) bool { + if !push { + genDecl = nil + return false + } + if code.IsInTest(pass, node) { + return false + } + + switch node := node.(type) { + case *ast.GenDecl: + if node.Tok == token.IMPORT { + return false + } + genDecl = node + return true + case *ast.TypeSpec: + if !ast.IsExported(node.Name.Name) { + return false + } + + doc := node.Doc + text, ok := docText(doc) + if !ok { + if len(genDecl.Specs) != 1 { + // more than one spec in the GenDecl, don't validate the + // docstring + return false + } + if genDecl.Lparen.IsValid() { + // 'type ( T )' is weird, don't guess the user's intention + return false + } + doc = genDecl.Doc + text, ok = docText(doc) + if !ok { + return false + } + } + + // Check comment before we strip articles in case the type's name is an article. + if strings.HasPrefix(text, node.Name.Name+" ") { + return false + } + + s := text + articles := [...]string{"A", "An", "The"} + for _, a := range articles { + if strings.HasPrefix(s, a+" ") { + s = s[len(a)+1:] + break + } + } + if !strings.HasPrefix(s, node.Name.Name+" ") { + report.Report(pass, doc, fmt.Sprintf(`comment on exported type %s should be of the form "%s ..." (with optional leading article)`, node.Name.Name, node.Name.Name), report.FilterGenerated()) + } + return false + case *ast.FuncLit, *ast.FuncDecl: + return false + default: + lint.ExhaustiveTypeSwitch(node) + return false + } + } + + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Nodes([]ast.Node{(*ast.GenDecl)(nil), (*ast.TypeSpec)(nil), (*ast.FuncLit)(nil), (*ast.FuncDecl)(nil)}, fn) + return nil, nil +} + +func docText(doc *ast.CommentGroup) (string, bool) { + if doc == nil { + return "", false + } + // We trim spaces primarily because of /**/ style comments, which often have leading space. + text := strings.TrimSpace(doc.Text()) + return text, text != "" +} diff --git a/tools/vendor/honnef.co/go/tools/stylecheck/st1022/st1022.go b/tools/vendor/honnef.co/go/tools/stylecheck/st1022/st1022.go new file mode 100644 index 0000000000..d377119883 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/stylecheck/st1022/st1022.go @@ -0,0 +1,105 @@ +package st1022 + +import ( + "fmt" + "go/ast" + "go/token" + "strings" + + "honnef.co/go/tools/analysis/code" + "honnef.co/go/tools/analysis/facts/generated" + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/analysis/report" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/inspector" +) + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: &analysis.Analyzer{ + Name: "ST1022", + Run: run, + Requires: []*analysis.Analyzer{generated.Analyzer, inspect.Analyzer}, + }, + Doc: &lint.RawDocumentation{ + Title: "The documentation of an exported variable or constant should start with variable's name", + Text: `Doc comments work best as complete sentences, which +allow a wide variety of automated presentations. The first sentence +should be a one-sentence summary that starts with the name being +declared. + +If every doc comment begins with the name of the item it describes, +you can use the \'doc\' subcommand of the \'go\' tool and run the output +through grep. + +See https://go.dev/doc/effective_go#commentary for more +information on how to write good documentation.`, + Since: "2020.1", + NonDefault: true, + MergeIf: lint.MergeIfAny, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer + +func run(pass *analysis.Pass) (interface{}, error) { + var genDecl *ast.GenDecl + fn := func(node ast.Node, push bool) bool { + if !push { + genDecl = nil + return false + } + if code.IsInTest(pass, node) { + return false + } + + switch node := node.(type) { + case *ast.GenDecl: + if node.Tok == token.IMPORT { + return false + } + genDecl = node + return true + case *ast.ValueSpec: + if genDecl.Lparen.IsValid() || len(node.Names) > 1 { + // Don't try to guess the user's intention + return false + } + name := node.Names[0].Name + if !ast.IsExported(name) { + return false + } + text, ok := docText(genDecl.Doc) + if !ok { + return false + } + prefix := name + " " + if !strings.HasPrefix(text, prefix) { + kind := "var" + if genDecl.Tok == token.CONST { + kind = "const" + } + report.Report(pass, genDecl.Doc, fmt.Sprintf(`comment on exported %s %s should be of the form "%s..."`, kind, name, prefix), report.FilterGenerated()) + } + return false + case *ast.FuncLit, *ast.FuncDecl: + return false + default: + lint.ExhaustiveTypeSwitch(node) + return false + } + } + + pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Nodes([]ast.Node{(*ast.GenDecl)(nil), (*ast.ValueSpec)(nil), (*ast.FuncLit)(nil), (*ast.FuncDecl)(nil)}, fn) + return nil, nil +} + +func docText(doc *ast.CommentGroup) (string, bool) { + if doc == nil { + return "", false + } + // We trim spaces primarily because of /**/ style comments, which often have leading space. + text := strings.TrimSpace(doc.Text()) + return text, text != "" +} diff --git a/tools/vendor/honnef.co/go/tools/stylecheck/st1023/st1023.go b/tools/vendor/honnef.co/go/tools/stylecheck/st1023/st1023.go new file mode 100644 index 0000000000..c1ab2c07f3 --- /dev/null +++ b/tools/vendor/honnef.co/go/tools/stylecheck/st1023/st1023.go @@ -0,0 +1,22 @@ +package st1023 + +import ( + "honnef.co/go/tools/analysis/lint" + "honnef.co/go/tools/internal/sharedcheck" +) + +func init() { + SCAnalyzer.Analyzer.Name = "ST1023" +} + +var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ + Analyzer: sharedcheck.RedundantTypeInDeclarationChecker("should", false), + Doc: &lint.RawDocumentation{ + Title: "Redundant type in variable declaration", + Since: "2021.1", + NonDefault: true, + MergeIf: lint.MergeIfAll, + }, +}) + +var Analyzer = SCAnalyzer.Analyzer diff --git a/tools/vendor/honnef.co/go/tools/unused/implements.go b/tools/vendor/honnef.co/go/tools/unused/implements.go index 2a202c6d73..05f87bbbc0 100644 --- a/tools/vendor/honnef.co/go/tools/unused/implements.go +++ b/tools/vendor/honnef.co/go/tools/unused/implements.go @@ -1,6 +1,8 @@ package unused -import "go/types" +import ( + "go/types" +) // lookupMethod returns the index of and method with matching package and name, or (-1, nil). func lookupMethod(T *types.Interface, pkg *types.Package, name string) (int, *types.Func) { @@ -60,6 +62,7 @@ func implements(V types.Type, T *types.Interface, msV *types.MethodSet) ([]*type // A concrete type implements T if it implements all methods of T. var sels []*types.Selection + var c methodsChecker for i := 0; i < T.NumMethods(); i++ { m := T.Method(i) sel := msV.Lookup(m.Pkg(), m.Name()) @@ -72,7 +75,7 @@ func implements(V types.Type, T *types.Interface, msV *types.MethodSet) ([]*type return nil, false } - if !types.Identical(f.Type(), m.Type()) { + if !c.methodIsCompatible(f, m) { return nil, false } @@ -80,3 +83,69 @@ func implements(V types.Type, T *types.Interface, msV *types.MethodSet) ([]*type } return sels, true } + +type methodsChecker struct { + typeParams map[*types.TypeParam]types.Type +} + +// Currently, this doesn't support methods like `foo(x []T)`. +func (c *methodsChecker) methodIsCompatible(implFunc *types.Func, interfaceFunc *types.Func) bool { + if types.Identical(implFunc.Type(), interfaceFunc.Type()) { + return true + } + implSig, implOk := implFunc.Type().(*types.Signature) + interfaceSig, interfaceOk := interfaceFunc.Type().(*types.Signature) + if !implOk || !interfaceOk { + // probably not reachable. handle conservatively. + return false + } + + if !c.typesAreCompatible(implSig.Params(), interfaceSig.Params()) { + return false + } + + if !c.typesAreCompatible(implSig.Results(), interfaceSig.Results()) { + return false + } + + return true +} + +func (c *methodsChecker) typesAreCompatible(implTypes, interfaceTypes *types.Tuple) bool { + if implTypes.Len() != interfaceTypes.Len() { + return false + } + for i := 0; i < implTypes.Len(); i++ { + if !c.typeIsCompatible(implTypes.At(i).Type(), interfaceTypes.At(i).Type()) { + return false + } + } + return true +} + +func (c *methodsChecker) typeIsCompatible(implType, interfaceType types.Type) bool { + if types.Identical(implType, interfaceType) { + return true + } + // We only support trivial use of type parameters. This isn't fully compatible with compiler type checking yet. + tp, ok := interfaceType.(*types.TypeParam) + if !ok { + return false + } + if c.typeParams == nil { + c.typeParams = make(map[*types.TypeParam]types.Type) + } + if c.typeParams[tp] == nil { + if !satisfiesConstraint(implType, tp) { + return false + } + c.typeParams[tp] = implType + return true + } + return types.Identical(c.typeParams[tp], implType) +} + +func satisfiesConstraint(t types.Type, tp *types.TypeParam) bool { + bound := tp.Constraint().Underlying().(*types.Interface) + return types.Satisfies(t, bound) +} diff --git a/tools/vendor/honnef.co/go/tools/unused/unused.go b/tools/vendor/honnef.co/go/tools/unused/unused.go index edd2630759..46c147e1a1 100644 --- a/tools/vendor/honnef.co/go/tools/unused/unused.go +++ b/tools/vendor/honnef.co/go/tools/unused/unused.go @@ -168,7 +168,7 @@ type Result struct { } var Analyzer = &lint.Analyzer{ - Doc: &lint.Documentation{ + Doc: &lint.RawDocumentation{ Title: "Unused code", }, Analyzer: &analysis.Analyzer{ @@ -344,7 +344,7 @@ func (g *graph) objectToObject(obj types.Object) Object { } name := obj.Name() if sig, ok := obj.Type().(*types.Signature); ok && sig.Recv() != nil { - switch sig.Recv().Type().(type) { + switch types.Unalias(sig.Recv().Type()).(type) { case *types.Named, *types.Pointer: typ := types.TypeString(sig.Recv().Type(), func(*types.Package) string { return "" }) if len(typ) > 0 && typ[0] == '*' { @@ -534,6 +534,19 @@ func (g *graph) entry() { } } + // We use a normal map instead of a typeutil.Map because we deduplicate + // these on a best effort basis, as an optimization. + allInterfaces := make(map[*types.Interface]struct{}) + for _, typ := range g.interfaceTypes { + allInterfaces[typ] = struct{}{} + } + for _, ins := range g.info.Instances { + if typ, ok := ins.Type.(*types.Named); ok && typ.Obj().Pkg() == g.pkg { + if iface, ok := typ.Underlying().(*types.Interface); ok { + allInterfaces[iface] = struct{}{} + } + } + } processMethodSet := func(named *types.TypeName, ms *types.MethodSet) { if g.opts.ExportedIsUsed { for i := 0; i < ms.Len(); i++ { @@ -552,7 +565,7 @@ func (g *graph) entry() { // (8.0) handle interfaces // // We don't care about interfaces implementing interfaces; all their methods are already used, anyway - for _, iface := range g.interfaceTypes { + for iface := range allInterfaces { if sels, ok := implements(named.Type(), iface, ms); ok { for _, sel := range sels { // (8.2) any concrete type implements all known interfaces @@ -628,7 +641,7 @@ func (g *graph) entry() { // use methods and fields of ignored types if obj, ok := obj.(*types.TypeName); ok { if obj.IsAlias() { - if typ, ok := obj.Type().(*types.Named); ok && (g.opts.ExportedIsUsed && typ.Obj().Pkg() != obj.Pkg() || typ.Obj().Pkg() == nil) { + if typ, ok := types.Unalias(obj.Type()).(*types.Named); ok && (g.opts.ExportedIsUsed && typ.Obj().Pkg() != obj.Pkg() || typ.Obj().Pkg() == nil) { // This is an alias of a named type in another package. // Don't walk its fields or methods; we don't have to. // @@ -637,7 +650,7 @@ func (g *graph) entry() { continue } } - if typ, ok := obj.Type().(*types.Named); ok { + if typ, ok := types.Unalias(obj.Type()).(*types.Named); ok { for i := 0; i < typ.NumMethods(); i++ { g.use(typ.Method(i), nil) } @@ -960,7 +973,7 @@ func (g *graph) write(node ast.Node, by types.Object) { case *ast.SelectorExpr: if g.opts.FieldWritesAreUses { - // Writing to a field constitutes a use. See https://staticcheck.io/issues/288 for some discussion on that. + // Writing to a field constitutes a use. See https://staticcheck.dev/issues/288 for some discussion on that. // // This code can also get triggered by qualified package variables, in which case it doesn't matter what we do, // because the object is in another package. @@ -1457,7 +1470,7 @@ func isNoCopyType(typ types.Type) bool { return false } - named, ok := typ.(*types.Named) + named, ok := types.Unalias(typ).(*types.Named) if !ok { return false } diff --git a/tools/vendor/modules.txt b/tools/vendor/modules.txt index 891f40a925..9afb3391f4 100644 --- a/tools/vendor/modules.txt +++ b/tools/vendor/modules.txt @@ -7,7 +7,7 @@ # github.com/4meepo/tagalign v1.3.4 ## explicit; go 1.19 github.com/4meepo/tagalign -# github.com/Abirdcfly/dupword v0.0.14 +# github.com/Abirdcfly/dupword v0.1.1 ## explicit; go 1.20 github.com/Abirdcfly/dupword # github.com/Antonboom/errname v0.1.13 @@ -16,31 +16,32 @@ github.com/Antonboom/errname/pkg/analyzer # github.com/Antonboom/nilnil v0.1.9 ## explicit; go 1.20 github.com/Antonboom/nilnil/pkg/analyzer -# github.com/Antonboom/testifylint v1.3.1 +# github.com/Antonboom/testifylint v1.4.3 ## explicit; go 1.20 github.com/Antonboom/testifylint/analyzer github.com/Antonboom/testifylint/internal/analysisutil github.com/Antonboom/testifylint/internal/checkers +github.com/Antonboom/testifylint/internal/checkers/printf github.com/Antonboom/testifylint/internal/config github.com/Antonboom/testifylint/internal/testify -# github.com/BurntSushi/toml v1.4.0 +# github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c ## explicit; go 1.18 github.com/BurntSushi/toml github.com/BurntSushi/toml/internal -# github.com/Crocmagnon/fatcontext v0.2.2 -## explicit; go 1.21 +# github.com/Crocmagnon/fatcontext v0.5.2 +## explicit; go 1.22.0 github.com/Crocmagnon/fatcontext/pkg/analyzer # github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 ## explicit; go 1.13 github.com/Djarvur/go-err113 -# github.com/GaijinEntertainment/go-exhaustruct/v3 v3.2.0 +# github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0 ## explicit; go 1.21 github.com/GaijinEntertainment/go-exhaustruct/v3/analyzer github.com/GaijinEntertainment/go-exhaustruct/v3/internal/comment github.com/GaijinEntertainment/go-exhaustruct/v3/internal/pattern github.com/GaijinEntertainment/go-exhaustruct/v3/internal/structure -# github.com/Masterminds/semver/v3 v3.2.1 -## explicit; go 1.18 +# github.com/Masterminds/semver/v3 v3.3.0 +## explicit; go 1.21 github.com/Masterminds/semver/v3 # github.com/OpenPeeDeeP/depguard/v2 v2.2.0 ## explicit; go 1.20 @@ -73,7 +74,7 @@ github.com/bkielbasa/cyclop/pkg/analyzer # github.com/blizzy78/varnamelen v0.8.0 ## explicit; go 1.16 github.com/blizzy78/varnamelen -# github.com/bombsimon/wsl/v4 v4.2.1 +# github.com/bombsimon/wsl/v4 v4.4.1 ## explicit; go 1.21 github.com/bombsimon/wsl/v4 # github.com/breml/bidichk v0.2.7 @@ -117,14 +118,14 @@ github.com/charithe/durationcheck # github.com/chavacava/garif v0.1.0 ## explicit; go 1.16 github.com/chavacava/garif -# github.com/ckaznocha/intrange v0.1.2 -## explicit; go 1.21 +# github.com/ckaznocha/intrange v0.2.0 +## explicit; go 1.22 github.com/ckaznocha/intrange # github.com/curioswitch/go-reassign v0.2.0 ## explicit; go 1.18 github.com/curioswitch/go-reassign github.com/curioswitch/go-reassign/internal/analyzer -# github.com/daixiang0/gci v0.13.4 +# github.com/daixiang0/gci v0.13.5 ## explicit; go 1.21 github.com/daixiang0/gci/pkg/config github.com/daixiang0/gci/pkg/format @@ -190,7 +191,7 @@ github.com/go-toolsmith/strparse # github.com/go-toolsmith/typep v1.1.0 ## explicit; go 1.16 github.com/go-toolsmith/typep -# github.com/go-viper/mapstructure/v2 v2.0.0 +# github.com/go-viper/mapstructure/v2 v2.1.0 ## explicit; go 1.18 github.com/go-viper/mapstructure/v2 github.com/go-viper/mapstructure/v2/internal/errors @@ -207,8 +208,8 @@ github.com/gobwas/glob/syntax/ast github.com/gobwas/glob/syntax/lexer github.com/gobwas/glob/util/runes github.com/gobwas/glob/util/strings -# github.com/gofrs/flock v0.8.1 -## explicit +# github.com/gofrs/flock v0.12.1 +## explicit; go 1.21.0 github.com/gofrs/flock # github.com/golang/protobuf v1.5.3 ## explicit; go 1.9 @@ -225,13 +226,13 @@ github.com/golangci/dupl/printer github.com/golangci/dupl/suffixtree github.com/golangci/dupl/syntax github.com/golangci/dupl/syntax/golang -# github.com/golangci/gofmt v0.0.0-20231018234816-f50ced29576e -## explicit; go 1.20 +# github.com/golangci/gofmt v0.0.0-20240816233607-d8596aa466a9 +## explicit; go 1.22 github.com/golangci/gofmt/gofmt github.com/golangci/gofmt/gofmt/internal/diff github.com/golangci/gofmt/goimports -# github.com/golangci/golangci-lint v1.59.1 -## explicit; go 1.21 +# github.com/golangci/golangci-lint v1.61.0 +## explicit; go 1.22.1 github.com/golangci/golangci-lint/cmd/golangci-lint github.com/golangci/golangci-lint/internal/cache github.com/golangci/golangci-lint/internal/errorutil @@ -436,7 +437,7 @@ github.com/jingyugao/rowserrcheck/passes/rowserr # github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af ## explicit; go 1.13 github.com/jirfag/go-printf-func-name/pkg/analyzer -# github.com/jjti/go-spancheck v0.6.1 +# github.com/jjti/go-spancheck v0.6.2 ## explicit; go 1.20 github.com/jjti/go-spancheck # github.com/julz/importas v0.1.0 @@ -507,8 +508,8 @@ github.com/mattn/go-runewidth # github.com/matttproud/golang_protobuf_extensions v1.0.1 ## explicit github.com/matttproud/golang_protobuf_extensions/pbutil -# github.com/mgechev/revive v1.3.7 -## explicit; go 1.20 +# github.com/mgechev/revive v1.3.9 +## explicit; go 1.21 github.com/mgechev/revive/config github.com/mgechev/revive/formatter github.com/mgechev/revive/internal/ifelse @@ -521,7 +522,7 @@ github.com/mitchellh/go-homedir # github.com/mitchellh/mapstructure v1.5.0 ## explicit; go 1.14 github.com/mitchellh/mapstructure -# github.com/moricho/tparallel v0.3.1 +# github.com/moricho/tparallel v0.3.2 ## explicit; go 1.20 github.com/moricho/tparallel github.com/moricho/tparallel/pkg/ssafunc @@ -529,8 +530,6 @@ github.com/moricho/tparallel/pkg/ssainstr # github.com/nakabonne/nestif v0.3.1 ## explicit; go 1.15 github.com/nakabonne/nestif -# github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e -## explicit; go 1.12 # github.com/nishanths/exhaustive v0.12.0 ## explicit; go 1.18 github.com/nishanths/exhaustive @@ -555,8 +554,8 @@ github.com/olekukonko/tablewriter # github.com/pelletier/go-toml v1.9.5 ## explicit; go 1.12 github.com/pelletier/go-toml -# github.com/pelletier/go-toml/v2 v2.2.2 -## explicit; go 1.16 +# github.com/pelletier/go-toml/v2 v2.2.3 +## explicit; go 1.21.0 github.com/pelletier/go-toml/v2 github.com/pelletier/go-toml/v2/internal/characters github.com/pelletier/go-toml/v2/internal/danger @@ -565,7 +564,7 @@ github.com/pelletier/go-toml/v2/unstable # github.com/pmezard/go-difflib v1.0.0 ## explicit github.com/pmezard/go-difflib/difflib -# github.com/polyfloyd/go-errorlint v1.5.2 +# github.com/polyfloyd/go-errorlint v1.6.0 ## explicit; go 1.20 github.com/polyfloyd/go-errorlint/errorlint # github.com/prometheus/client_golang v1.12.1 @@ -586,7 +585,7 @@ github.com/prometheus/common/model github.com/prometheus/procfs github.com/prometheus/procfs/internal/fs github.com/prometheus/procfs/internal/util -# github.com/quasilyte/go-ruleguard v0.4.2 +# github.com/quasilyte/go-ruleguard v0.4.3-0.20240823090925-0fe6f58b47b1 ## explicit; go 1.19 github.com/quasilyte/go-ruleguard/internal/goenv github.com/quasilyte/go-ruleguard/internal/golist @@ -621,8 +620,8 @@ github.com/quasilyte/stdinfo # github.com/randall77/makefat v0.0.0-20210315173500-7ddd0e42c844 ## explicit; go 1.13 github.com/randall77/makefat -# github.com/ryancurrah/gomodguard v1.3.2 -## explicit; go 1.21 +# github.com/ryancurrah/gomodguard v1.3.5 +## explicit; go 1.22.0 github.com/ryancurrah/gomodguard # github.com/ryanrolds/sqlclosecheck v0.5.1 ## explicit; go 1.20 @@ -637,12 +636,12 @@ github.com/santhosh-tekuri/jsonschema/v5/httploader # github.com/sashamelentyev/interfacebloat v1.1.0 ## explicit; go 1.18 github.com/sashamelentyev/interfacebloat/pkg/analyzer -# github.com/sashamelentyev/usestdlibvars v1.26.0 +# github.com/sashamelentyev/usestdlibvars v1.27.0 ## explicit; go 1.20 github.com/sashamelentyev/usestdlibvars/pkg/analyzer github.com/sashamelentyev/usestdlibvars/pkg/analyzer/internal/mapping -# github.com/securego/gosec/v2 v2.20.1-0.20240525090044-5f0084eb01a9 -## explicit; go 1.20 +# github.com/securego/gosec/v2 v2.21.2 +## explicit; go 1.22 github.com/securego/gosec/v2 github.com/securego/gosec/v2/analyzers github.com/securego/gosec/v2/cwe @@ -657,8 +656,8 @@ github.com/sirupsen/logrus # github.com/sivchari/containedctx v1.0.3 ## explicit; go 1.17 github.com/sivchari/containedctx -# github.com/sivchari/tenv v1.7.1 -## explicit; go 1.18 +# github.com/sivchari/tenv v1.10.0 +## explicit; go 1.21.0 github.com/sivchari/tenv # github.com/sonatard/noctx v0.0.2 ## explicit; go 1.20 @@ -676,7 +675,7 @@ github.com/spf13/afero/mem # github.com/spf13/cast v1.5.0 ## explicit; go 1.18 github.com/spf13/cast -# github.com/spf13/cobra v1.7.0 +# github.com/spf13/cobra v1.8.1 ## explicit; go 1.15 github.com/spf13/cobra # github.com/spf13/jwalterweatherman v1.1.0 @@ -712,13 +711,10 @@ github.com/stretchr/testify/mock # github.com/subosito/gotenv v1.4.1 ## explicit; go 1.18 github.com/subosito/gotenv -# github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c -## explicit -github.com/t-yuki/gocover-cobertura # github.com/tdakkota/asciicheck v0.2.0 ## explicit; go 1.18 github.com/tdakkota/asciicheck -# github.com/tetafro/godot v1.4.16 +# github.com/tetafro/godot v1.4.17 ## explicit; go 1.20 github.com/tetafro/godot # github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966 @@ -733,7 +729,7 @@ github.com/timonwong/loggercheck/internal/checkers/printf github.com/timonwong/loggercheck/internal/rules github.com/timonwong/loggercheck/internal/sets github.com/timonwong/loggercheck/internal/stringutil -# github.com/tomarrell/wrapcheck/v2 v2.8.3 +# github.com/tomarrell/wrapcheck/v2 v2.9.0 ## explicit; go 1.21 github.com/tomarrell/wrapcheck/v2/wrapcheck # github.com/tommy-muehle/go-mnd/v2 v2.5.1 @@ -747,8 +743,8 @@ github.com/ultraware/funlen # github.com/ultraware/whitespace v0.1.1 ## explicit; go 1.20 github.com/ultraware/whitespace -# github.com/uudashr/gocognit v1.1.2 -## explicit; go 1.16 +# github.com/uudashr/gocognit v1.1.3 +## explicit; go 1.18 github.com/uudashr/gocognit # github.com/xen0n/gosmopolitan v1.2.2 ## explicit; go 1.19 @@ -770,7 +766,7 @@ gitlab.com/bosi/decorder # go-simpler.org/musttag v0.12.2 ## explicit; go 1.20 go-simpler.org/musttag -# go-simpler.org/sloglint v0.7.1 +# go-simpler.org/sloglint v0.7.2 ## explicit; go 1.21 go-simpler.org/sloglint # go.uber.org/atomic v1.7.0 @@ -793,7 +789,7 @@ go.uber.org/zap/internal/bufferpool go.uber.org/zap/internal/color go.uber.org/zap/internal/exit go.uber.org/zap/zapcore -# golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc +# golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e ## explicit; go 1.20 golang.org/x/exp/constraints golang.org/x/exp/maps @@ -801,8 +797,8 @@ golang.org/x/exp/slices # golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f ## explicit; go 1.18 golang.org/x/exp/typeparams -# golang.org/x/mod v0.20.0 -## explicit; go 1.18 +# golang.org/x/mod v0.21.0 +## explicit; go 1.22.0 golang.org/x/mod/internal/lazyregexp golang.org/x/mod/modfile golang.org/x/mod/module @@ -811,11 +807,11 @@ golang.org/x/mod/semver ## explicit; go 1.18 golang.org/x/sync/errgroup golang.org/x/sync/semaphore -# golang.org/x/sys v0.23.0 +# golang.org/x/sys v0.25.0 ## explicit; go 1.18 golang.org/x/sys/unix golang.org/x/sys/windows -# golang.org/x/text v0.15.0 +# golang.org/x/text v0.18.0 ## explicit; go 1.18 golang.org/x/text/runes golang.org/x/text/transform @@ -901,14 +897,15 @@ golang.org/x/tools/internal/tokeninternal golang.org/x/tools/internal/typeparams golang.org/x/tools/internal/typesinternal golang.org/x/tools/internal/versions -# google.golang.org/protobuf v1.33.0 -## explicit; go 1.17 +# google.golang.org/protobuf v1.34.2 +## explicit; go 1.20 google.golang.org/protobuf/encoding/prototext google.golang.org/protobuf/encoding/protowire google.golang.org/protobuf/internal/descfmt google.golang.org/protobuf/internal/descopts google.golang.org/protobuf/internal/detrand google.golang.org/protobuf/internal/editiondefaults +google.golang.org/protobuf/internal/editionssupport google.golang.org/protobuf/internal/encoding/defval google.golang.org/protobuf/internal/encoding/messageset google.golang.org/protobuf/internal/encoding/tag @@ -935,8 +932,6 @@ google.golang.org/protobuf/types/gofeaturespb google.golang.org/protobuf/types/known/anypb google.golang.org/protobuf/types/known/durationpb google.golang.org/protobuf/types/known/timestamppb -# gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f -## explicit # gopkg.in/ini.v1 v1.67.0 ## explicit gopkg.in/ini.v1 @@ -946,8 +941,9 @@ gopkg.in/yaml.v2 # gopkg.in/yaml.v3 v3.0.1 ## explicit gopkg.in/yaml.v3 -# honnef.co/go/tools v0.4.7 -## explicit; go 1.19 +# honnef.co/go/tools v0.5.1 +## explicit; go 1.22.1 +honnef.co/go/tools/analysis/callcheck honnef.co/go/tools/analysis/code honnef.co/go/tools/analysis/edit honnef.co/go/tools/analysis/facts/deprecated @@ -970,14 +966,162 @@ honnef.co/go/tools/knowledge honnef.co/go/tools/pattern honnef.co/go/tools/printf honnef.co/go/tools/simple +honnef.co/go/tools/simple/s1000 +honnef.co/go/tools/simple/s1001 +honnef.co/go/tools/simple/s1002 +honnef.co/go/tools/simple/s1003 +honnef.co/go/tools/simple/s1004 +honnef.co/go/tools/simple/s1005 +honnef.co/go/tools/simple/s1006 +honnef.co/go/tools/simple/s1007 +honnef.co/go/tools/simple/s1008 +honnef.co/go/tools/simple/s1009 +honnef.co/go/tools/simple/s1010 +honnef.co/go/tools/simple/s1011 +honnef.co/go/tools/simple/s1012 +honnef.co/go/tools/simple/s1016 +honnef.co/go/tools/simple/s1017 +honnef.co/go/tools/simple/s1018 +honnef.co/go/tools/simple/s1019 +honnef.co/go/tools/simple/s1020 +honnef.co/go/tools/simple/s1021 +honnef.co/go/tools/simple/s1023 +honnef.co/go/tools/simple/s1024 +honnef.co/go/tools/simple/s1025 +honnef.co/go/tools/simple/s1028 +honnef.co/go/tools/simple/s1029 +honnef.co/go/tools/simple/s1030 +honnef.co/go/tools/simple/s1031 +honnef.co/go/tools/simple/s1032 +honnef.co/go/tools/simple/s1033 +honnef.co/go/tools/simple/s1034 +honnef.co/go/tools/simple/s1035 +honnef.co/go/tools/simple/s1036 +honnef.co/go/tools/simple/s1037 +honnef.co/go/tools/simple/s1038 +honnef.co/go/tools/simple/s1039 +honnef.co/go/tools/simple/s1040 honnef.co/go/tools/staticcheck honnef.co/go/tools/staticcheck/fakejson honnef.co/go/tools/staticcheck/fakereflect honnef.co/go/tools/staticcheck/fakexml +honnef.co/go/tools/staticcheck/sa1000 +honnef.co/go/tools/staticcheck/sa1001 +honnef.co/go/tools/staticcheck/sa1002 +honnef.co/go/tools/staticcheck/sa1003 +honnef.co/go/tools/staticcheck/sa1004 +honnef.co/go/tools/staticcheck/sa1005 +honnef.co/go/tools/staticcheck/sa1006 +honnef.co/go/tools/staticcheck/sa1007 +honnef.co/go/tools/staticcheck/sa1008 +honnef.co/go/tools/staticcheck/sa1010 +honnef.co/go/tools/staticcheck/sa1011 +honnef.co/go/tools/staticcheck/sa1012 +honnef.co/go/tools/staticcheck/sa1013 +honnef.co/go/tools/staticcheck/sa1014 +honnef.co/go/tools/staticcheck/sa1015 +honnef.co/go/tools/staticcheck/sa1016 +honnef.co/go/tools/staticcheck/sa1017 +honnef.co/go/tools/staticcheck/sa1018 +honnef.co/go/tools/staticcheck/sa1019 +honnef.co/go/tools/staticcheck/sa1020 +honnef.co/go/tools/staticcheck/sa1021 +honnef.co/go/tools/staticcheck/sa1023 +honnef.co/go/tools/staticcheck/sa1024 +honnef.co/go/tools/staticcheck/sa1025 +honnef.co/go/tools/staticcheck/sa1026 +honnef.co/go/tools/staticcheck/sa1027 +honnef.co/go/tools/staticcheck/sa1028 +honnef.co/go/tools/staticcheck/sa1029 +honnef.co/go/tools/staticcheck/sa1030 +honnef.co/go/tools/staticcheck/sa1031 +honnef.co/go/tools/staticcheck/sa1032 +honnef.co/go/tools/staticcheck/sa2000 +honnef.co/go/tools/staticcheck/sa2001 +honnef.co/go/tools/staticcheck/sa2002 +honnef.co/go/tools/staticcheck/sa2003 +honnef.co/go/tools/staticcheck/sa3000 +honnef.co/go/tools/staticcheck/sa3001 +honnef.co/go/tools/staticcheck/sa4000 +honnef.co/go/tools/staticcheck/sa4001 +honnef.co/go/tools/staticcheck/sa4003 +honnef.co/go/tools/staticcheck/sa4004 +honnef.co/go/tools/staticcheck/sa4005 +honnef.co/go/tools/staticcheck/sa4006 +honnef.co/go/tools/staticcheck/sa4008 +honnef.co/go/tools/staticcheck/sa4009 +honnef.co/go/tools/staticcheck/sa4010 +honnef.co/go/tools/staticcheck/sa4011 +honnef.co/go/tools/staticcheck/sa4012 +honnef.co/go/tools/staticcheck/sa4013 +honnef.co/go/tools/staticcheck/sa4014 +honnef.co/go/tools/staticcheck/sa4015 +honnef.co/go/tools/staticcheck/sa4016 +honnef.co/go/tools/staticcheck/sa4017 +honnef.co/go/tools/staticcheck/sa4018 +honnef.co/go/tools/staticcheck/sa4019 +honnef.co/go/tools/staticcheck/sa4020 +honnef.co/go/tools/staticcheck/sa4021 +honnef.co/go/tools/staticcheck/sa4022 +honnef.co/go/tools/staticcheck/sa4023 +honnef.co/go/tools/staticcheck/sa4024 +honnef.co/go/tools/staticcheck/sa4025 +honnef.co/go/tools/staticcheck/sa4026 +honnef.co/go/tools/staticcheck/sa4027 +honnef.co/go/tools/staticcheck/sa4028 +honnef.co/go/tools/staticcheck/sa4029 +honnef.co/go/tools/staticcheck/sa4030 +honnef.co/go/tools/staticcheck/sa4031 +honnef.co/go/tools/staticcheck/sa4032 +honnef.co/go/tools/staticcheck/sa5000 +honnef.co/go/tools/staticcheck/sa5001 +honnef.co/go/tools/staticcheck/sa5002 +honnef.co/go/tools/staticcheck/sa5003 +honnef.co/go/tools/staticcheck/sa5004 +honnef.co/go/tools/staticcheck/sa5005 +honnef.co/go/tools/staticcheck/sa5007 +honnef.co/go/tools/staticcheck/sa5008 +honnef.co/go/tools/staticcheck/sa5009 +honnef.co/go/tools/staticcheck/sa5010 +honnef.co/go/tools/staticcheck/sa5011 +honnef.co/go/tools/staticcheck/sa5012 +honnef.co/go/tools/staticcheck/sa6000 +honnef.co/go/tools/staticcheck/sa6001 +honnef.co/go/tools/staticcheck/sa6002 +honnef.co/go/tools/staticcheck/sa6003 +honnef.co/go/tools/staticcheck/sa6005 +honnef.co/go/tools/staticcheck/sa6006 +honnef.co/go/tools/staticcheck/sa9001 +honnef.co/go/tools/staticcheck/sa9002 +honnef.co/go/tools/staticcheck/sa9003 +honnef.co/go/tools/staticcheck/sa9004 +honnef.co/go/tools/staticcheck/sa9005 +honnef.co/go/tools/staticcheck/sa9006 +honnef.co/go/tools/staticcheck/sa9007 +honnef.co/go/tools/staticcheck/sa9008 +honnef.co/go/tools/staticcheck/sa9009 honnef.co/go/tools/stylecheck +honnef.co/go/tools/stylecheck/st1000 +honnef.co/go/tools/stylecheck/st1001 +honnef.co/go/tools/stylecheck/st1003 +honnef.co/go/tools/stylecheck/st1005 +honnef.co/go/tools/stylecheck/st1006 +honnef.co/go/tools/stylecheck/st1008 +honnef.co/go/tools/stylecheck/st1011 +honnef.co/go/tools/stylecheck/st1012 +honnef.co/go/tools/stylecheck/st1013 +honnef.co/go/tools/stylecheck/st1015 +honnef.co/go/tools/stylecheck/st1016 +honnef.co/go/tools/stylecheck/st1017 +honnef.co/go/tools/stylecheck/st1018 +honnef.co/go/tools/stylecheck/st1019 +honnef.co/go/tools/stylecheck/st1020 +honnef.co/go/tools/stylecheck/st1021 +honnef.co/go/tools/stylecheck/st1022 +honnef.co/go/tools/stylecheck/st1023 honnef.co/go/tools/unused -# mvdan.cc/gofumpt v0.6.0 -## explicit; go 1.20 +# mvdan.cc/gofumpt v0.7.0 +## explicit; go 1.22 mvdan.cc/gofumpt/format mvdan.cc/gofumpt/internal/govendor/go/doc/comment mvdan.cc/gofumpt/internal/govendor/go/format diff --git a/tools/vendor/mvdan.cc/gofumpt/format/format.go b/tools/vendor/mvdan.cc/gofumpt/format/format.go index 7316eb78b8..e81420cf1c 100644 --- a/tools/vendor/mvdan.cc/gofumpt/format/format.go +++ b/tools/vendor/mvdan.cc/gofumpt/format/format.go @@ -12,9 +12,11 @@ import ( "go/ast" "go/parser" "go/token" + goversion "go/version" "os" "reflect" "regexp" + "slices" "sort" "strconv" "strings" @@ -22,7 +24,6 @@ import ( "unicode/utf8" "github.com/google/go-cmp/cmp" - "golang.org/x/mod/semver" "golang.org/x/tools/go/ast/astutil" "mvdan.cc/gofumpt/internal/govendor/go/format" @@ -31,26 +32,25 @@ import ( // Options is the set of formatting options which affect gofumpt. type Options struct { - // TODO: link to the go/version docs once Go 1.22 is out. - // The old semver docs said: - // - // LangVersion is treated as a semantic version, which may start with a "v" - // prefix. Like Go versions, it may also be incomplete; "1.14" is equivalent - // to "1.14.0". When empty, it is equivalent to "v1", to not use language - // features which could break programs. - // LangVersion is the Go version a piece of code is written in. // The version is used to decide whether to apply formatting // rules which require new language features. - // When inside a Go module, LangVersion should typically be: + // When empty, a default of go1 is assumed. + // Otherwise, the version must satisfy [go/version.IsValid]. + // + // When formatting a Go module, LangVersion should typically be + // + // go list -m -f {{.GoVersion}} // - // go mod edit -json | jq -r '.Go' + // with a "go" prefix, or the equivalent from `go mod edit -json`. LangVersion string // ModulePath corresponds to the Go module path which contains the source - // code being formatted. When inside a Go module, ModulePath should be: + // code being formatted. When formatting a Go module, ModulePath should be // - // go mod edit -json | jq -r '.Module.Path' + // go list -m -f {{.Path}} + // + // or the equivalent from `go mod edit -json`. // // ModulePath is used for formatting decisions like what import paths are // considered to be not part of the standard library. When empty, the source @@ -85,26 +85,21 @@ func Source(src []byte, opts Options) ([]byte, error) { return buf.Bytes(), nil } -var rxGoVersionMajorMinor = regexp.MustCompile(`^(v|go)?([1-9]+)\.([0-9]+)`) - // File modifies a file and fset in place to follow gofumpt's format. The // changes might include manipulating adding or removing newlines in fset, // modifying the position of nodes, or modifying literal values. func File(fset *token.FileSet, file *ast.File, opts Options) { simplify(file) - // TODO: replace this hacky mess with go/version once we can rely on Go 1.22, - // as well as replacing our uses of the semver package. - // In particular, we likely want to allow any of 1.21, 1.21.2, or go1.21rc3, - // but we can rely on go/version.Lang to validate and normalize. if opts.LangVersion == "" { - opts.LangVersion = "v1.0" - } - m := rxGoVersionMajorMinor.FindStringSubmatch(opts.LangVersion) - if m == nil { - panic(fmt.Sprintf("invalid Go version: %q", opts.LangVersion)) + opts.LangVersion = "go1" + } else { + lang := goversion.Lang(opts.LangVersion) + if lang == "" { + panic(fmt.Sprintf("invalid Go version: %q", opts.LangVersion)) + } + opts.LangVersion = lang } - opts.LangVersion = "v" + m[2] + "." + m[3] f := &fumpter{ file: fset.File(file.Pos()), fset: fset, @@ -226,26 +221,14 @@ func (f *fumpter) inlineComment(pos token.Pos) *ast.Comment { func (f *fumpter) addNewline(at token.Pos) { offset := f.Offset(at) - // TODO: replace with the new Lines method once we require Go 1.21 or later - field := reflect.ValueOf(f.file).Elem().FieldByName("lines") - n := field.Len() - lines := make([]int, 0, n+1) - for i := 0; i < n; i++ { - cur := int(field.Index(i).Int()) - if offset == cur { - // This newline already exists; do nothing. Duplicate - // newlines can't exist. - return - } - if offset >= 0 && offset < cur { - lines = append(lines, offset) - offset = -1 - } - lines = append(lines, cur) - } - if offset >= 0 { - lines = append(lines, offset) + lines := f.file.Lines() + i, exists := slices.BinarySearch(lines, offset) + if exists { + // This newline already exists; do nothing. Duplicate + // newlines can't exist. + return } + lines = slices.Insert(lines, i, offset) if !f.file.SetLines(lines) { panic(fmt.Sprintf("could not set lines to %v", lines)) } @@ -691,8 +674,8 @@ func (f *fumpter) applyPre(c *astutil.Cursor) { } case *ast.BasicLit: - // Octal number literals were introduced in 1.13. - if semver.Compare(f.LangVersion, "v1.13") >= 0 { + // Octal number literals were introduced in Go 1.13. + if goversion.Compare(f.LangVersion, "go1.13") >= 0 { if node.Kind == token.INT && rxOctalInteger.MatchString(node.Value) { node.Value = "0o" + node.Value[1:] c.Replace(node) @@ -978,10 +961,8 @@ func (f *fumpter) joinStdImports(d *ast.GenDecl) { case periodIndex > 0 && (slashIndex == -1 || periodIndex < slashIndex), // "test" and "example" are reserved as per golang.org/issue/37641. - // "internal" is unreachable. strings.HasPrefix(path, "test/"), strings.HasPrefix(path, "example/"), - strings.HasPrefix(path, "internal/"), // See if we match modulePrefix; see its documentation above. // We match either exactly or with a slash suffix, diff --git a/tools/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/html.go b/tools/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/html.go index bc076f6a58..9244509e00 100644 --- a/tools/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/html.go +++ b/tools/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/html.go @@ -10,13 +10,13 @@ import ( "strconv" ) -// An htmlPrinter holds the state needed for printing a Doc as HTML. +// An htmlPrinter holds the state needed for printing a [Doc] as HTML. type htmlPrinter struct { *Printer tight bool } -// HTML returns an HTML formatting of the Doc. +// HTML returns an HTML formatting of the [Doc]. // See the [Printer] documentation for ways to customize the HTML output. func (p *Printer) HTML(d *Doc) []byte { hp := &htmlPrinter{Printer: p} diff --git a/tools/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/parse.go b/tools/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/parse.go index 372577b2b3..bd42c55ecc 100644 --- a/tools/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/parse.go +++ b/tools/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/parse.go @@ -5,7 +5,7 @@ package comment import ( - "sort" + "slices" "strings" "unicode" "unicode/utf8" @@ -176,7 +176,7 @@ type DocLink struct { func (*DocLink) text() {} // A Parser is a doc comment parser. -// The fields in the struct can be filled in before calling Parse +// The fields in the struct can be filled in before calling [Parser.Parse] // in order to customize the details of the parsing process. type Parser struct { // Words is a map of Go identifier words that @@ -260,14 +260,12 @@ func (d *parseDoc) lookupPkg(pkg string) (importPath string, ok bool) { } func isStdPkg(path string) bool { - // TODO(rsc): Use sort.Find once we don't have to worry about - // copying this code into older Go environments. - i := sort.Search(len(stdPkgs), func(i int) bool { return stdPkgs[i] >= path }) - return i < len(stdPkgs) && stdPkgs[i] == path + _, ok := slices.BinarySearch(stdPkgs, path) + return ok } // DefaultLookupPackage is the default package lookup -// function, used when [Parser].LookupPackage is nil. +// function, used when [Parser.LookupPackage] is nil. // It recognizes names of the packages from the standard // library with single-element import paths, such as math, // which would otherwise be impossible to name. @@ -281,7 +279,7 @@ func DefaultLookupPackage(name string) (importPath string, ok bool) { return "", false } -// Parse parses the doc comment text and returns the *Doc form. +// Parse parses the doc comment text and returns the *[Doc] form. // Comment markers (/* // and */) in the text must have already been removed. func (p *Parser) Parse(text string) *Doc { lines := unindent(strings.Split(text, "\n")) diff --git a/tools/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/print.go b/tools/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/print.go index e1c070d5a5..a6ae8210b6 100644 --- a/tools/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/print.go +++ b/tools/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/print.go @@ -150,7 +150,7 @@ type commentPrinter struct { *Printer } -// Comment returns the standard Go formatting of the Doc, +// Comment returns the standard Go formatting of the [Doc], // without any comment markers. func (p *Printer) Comment(d *Doc) []byte { cp := &commentPrinter{Printer: p} diff --git a/tools/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/std.go b/tools/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/std.go index d128eda8c5..35e26abb33 100644 --- a/tools/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/std.go +++ b/tools/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/std.go @@ -23,6 +23,7 @@ var stdPkgs = []string{ "html", "image", "io", + "iter", "log", "maps", "math", @@ -38,10 +39,12 @@ var stdPkgs = []string{ "sort", "strconv", "strings", + "structs", "sync", "syscall", "testing", "time", "unicode", + "unique", "unsafe", } diff --git a/tools/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/text.go b/tools/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/text.go index 6f9c2e201d..4e4214e08e 100644 --- a/tools/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/text.go +++ b/tools/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/text.go @@ -21,7 +21,7 @@ type textPrinter struct { width int } -// Text returns a textual formatting of the Doc. +// Text returns a textual formatting of the [Doc]. // See the [Printer] documentation for ways to customize the text output. func (p *Printer) Text(d *Doc) []byte { tp := &textPrinter{ diff --git a/tools/vendor/mvdan.cc/gofumpt/internal/govendor/go/format/format.go b/tools/vendor/mvdan.cc/gofumpt/internal/govendor/go/format/format.go index 5540686ed0..63e65e9053 100644 --- a/tools/vendor/mvdan.cc/gofumpt/internal/govendor/go/format/format.go +++ b/tools/vendor/mvdan.cc/gofumpt/internal/govendor/go/format/format.go @@ -43,11 +43,11 @@ const parserMode = parser.ParseComments | parser.SkipObjectResolution // Node formats node in canonical gofmt style and writes the result to dst. // -// The node type must be *ast.File, *printer.CommentedNode, []ast.Decl, -// []ast.Stmt, or assignment-compatible to ast.Expr, ast.Decl, ast.Spec, -// or ast.Stmt. Node does not modify node. Imports are not sorted for +// The node type must be *[ast.File], *[printer.CommentedNode], [][ast.Decl], +// [][ast.Stmt], or assignment-compatible to [ast.Expr], [ast.Decl], [ast.Spec], +// or [ast.Stmt]. Node does not modify node. Imports are not sorted for // nodes representing partial source files (for instance, if the node is -// not an *ast.File or a *printer.CommentedNode not wrapping an *ast.File). +// not an *[ast.File] or a *[printer.CommentedNode] not wrapping an *[ast.File]). // // The function may return early (before the entire result is written) // and return a formatting error, for instance due to an incorrect AST. diff --git a/tools/vendor/mvdan.cc/gofumpt/internal/govendor/go/printer/gobuild.go b/tools/vendor/mvdan.cc/gofumpt/internal/govendor/go/printer/gobuild.go index f00492d077..6f04cf6d6d 100644 --- a/tools/vendor/mvdan.cc/gofumpt/internal/govendor/go/printer/gobuild.go +++ b/tools/vendor/mvdan.cc/gofumpt/internal/govendor/go/printer/gobuild.go @@ -6,7 +6,7 @@ package printer import ( "go/build/constraint" - "sort" + "slices" "text/tabwriter" ) @@ -108,7 +108,7 @@ func (p *printer) fixGoBuildLines() { // Build sorted list of lines to delete from remainder of output. toDelete := append(p.goBuild, p.plusBuild...) - sort.Ints(toDelete) + slices.Sort(toDelete) // Collect output after insertion point, with lines deleted, into after. var after []byte diff --git a/tools/vendor/mvdan.cc/gofumpt/internal/govendor/go/printer/nodes.go b/tools/vendor/mvdan.cc/gofumpt/internal/govendor/go/printer/nodes.go index a58525b855..b8c7dedf04 100644 --- a/tools/vendor/mvdan.cc/gofumpt/internal/govendor/go/printer/nodes.go +++ b/tools/vendor/mvdan.cc/gofumpt/internal/govendor/go/printer/nodes.go @@ -44,10 +44,7 @@ import ( // linebreaks. At the moment there is no easy way to know about // future (not yet interspersed) comments in this function. func (p *printer) linebreak(line, min int, ws whiteSpace, newSection bool) (nbreaks int) { - n := nlimit(line - p.pos.Line) - if n < min { - n = min - } + n := max(nlimit(line-p.pos.Line), min) if n > 0 { p.print(ws) if newSection { @@ -670,9 +667,7 @@ func walkBinary(e *ast.BinaryExpr) (has4, has5 bool, maxProblem int) { h4, h5, mp := walkBinary(l) has4 = has4 || h4 has5 = has5 || h5 - if maxProblem < mp { - maxProblem = mp - } + maxProblem = max(maxProblem, mp) } switch r := e.Y.(type) { @@ -685,9 +680,7 @@ func walkBinary(e *ast.BinaryExpr) (has4, has5 bool, maxProblem int) { h4, h5, mp := walkBinary(r) has4 = has4 || h4 has5 = has5 || h5 - if maxProblem < mp { - maxProblem = mp - } + maxProblem = max(maxProblem, mp) case *ast.StarExpr: if e.Op == token.QUO { // `*/` @@ -699,9 +692,7 @@ func walkBinary(e *ast.BinaryExpr) (has4, has5 bool, maxProblem int) { case "/*", "&&", "&^": maxProblem = 5 case "++", "--": - if maxProblem < 4 { - maxProblem = 4 - } + maxProblem = max(maxProblem, 4) } } return @@ -983,15 +974,24 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) { if len(x.Args) > 1 { depth++ } - var wasIndented bool - if _, ok := x.Fun.(*ast.FuncType); ok { - // conversions to literal function types require parentheses around the type + + // Conversions to literal function types or <-chan + // types require parentheses around the type. + paren := false + switch t := x.Fun.(type) { + case *ast.FuncType: + paren = true + case *ast.ChanType: + paren = t.Dir == ast.RECV + } + if paren { p.print(token.LPAREN) - wasIndented = p.possibleSelectorExpr(x.Fun, token.HighestPrec, depth) + } + wasIndented := p.possibleSelectorExpr(x.Fun, token.HighestPrec, depth) + if paren { p.print(token.RPAREN) - } else { - wasIndented = p.possibleSelectorExpr(x.Fun, token.HighestPrec, depth) } + p.setPos(x.Lparen) p.print(token.LPAREN) if x.Ellipsis.IsValid() { @@ -1739,7 +1739,7 @@ func (p *printer) genDecl(d *ast.GenDecl) { p.setPos(d.Pos()) p.print(d.Tok, blank) - if d.Lparen.IsValid() || len(d.Specs) > 1 { + if d.Lparen.IsValid() || len(d.Specs) != 1 { // group of parenthesized declarations p.setPos(d.Lparen) p.print(token.LPAREN) diff --git a/tools/vendor/mvdan.cc/gofumpt/internal/govendor/go/printer/printer.go b/tools/vendor/mvdan.cc/gofumpt/internal/govendor/go/printer/printer.go index 2ab0278b08..11c056d6bf 100644 --- a/tools/vendor/mvdan.cc/gofumpt/internal/govendor/go/printer/printer.go +++ b/tools/vendor/mvdan.cc/gofumpt/internal/govendor/go/printer/printer.go @@ -860,10 +860,7 @@ func (p *printer) writeWhitespace(n int) { // nlimit limits n to maxNewlines. func nlimit(n int) int { - if n > maxNewlines { - n = maxNewlines - } - return n + return min(n, maxNewlines) } func mayCombine(prev token.Token, next byte) (b bool) { @@ -1412,7 +1409,7 @@ func (cfg *Config) fprint(output io.Writer, fset *token.FileSet, node any, nodeS } // A CommentedNode bundles an AST node and corresponding comments. -// It may be provided as argument to any of the Fprint functions. +// It may be provided as argument to any of the [Fprint] functions. type CommentedNode struct { Node any // *ast.File, or ast.Expr, ast.Decl, ast.Spec, or ast.Stmt Comments []*ast.CommentGroup @@ -1420,14 +1417,14 @@ type CommentedNode struct { // Fprint "pretty-prints" an AST node to output for a given configuration cfg. // Position information is interpreted relative to the file set fset. -// The node type must be *ast.File, *CommentedNode, []ast.Decl, []ast.Stmt, -// or assignment-compatible to ast.Expr, ast.Decl, ast.Spec, or ast.Stmt. +// The node type must be *[ast.File], *[CommentedNode], [][ast.Decl], [][ast.Stmt], +// or assignment-compatible to [ast.Expr], [ast.Decl], [ast.Spec], or [ast.Stmt]. func (cfg *Config) Fprint(output io.Writer, fset *token.FileSet, node any) error { return cfg.fprint(output, fset, node, make(map[ast.Node]int)) } // Fprint "pretty-prints" an AST node to output. -// It calls Config.Fprint with default settings. +// It calls [Config.Fprint] with default settings. // Note that gofmt uses tabs for indentation but spaces for alignment; // use format.Node (package mvdan.cc/gofumpt/internal/govendor/go/format) for output that matches gofmt. func Fprint(output io.Writer, fset *token.FileSet, node any) error {