diff --git a/go.mod b/go.mod index 1c505325..108dbdbe 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/Knetic/govaluate v3.0.0+incompatible github.com/dop251/goja v0.0.0-20220927172339-ea66e911853d github.com/dop251/goja_nodejs v0.0.0-20220905124449-678b33ca5009 - github.com/golangci/golangci-lint v1.52.2 + github.com/golangci/golangci-lint v1.53.0 github.com/google/go-cmp v0.5.9 github.com/onsi/ginkgo v1.16.5 github.com/onsi/ginkgo/v2 v2.9.7 @@ -37,9 +37,10 @@ require ( 4d63.com/gochecknoglobals v0.2.1 // indirect cloud.google.com/go/compute v1.18.0 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect + github.com/4meepo/tagalign v1.2.2 // indirect github.com/Abirdcfly/dupword v0.0.11 // indirect - github.com/Antonboom/errname v0.1.9 // indirect - github.com/Antonboom/nilnil v0.1.3 // indirect + github.com/Antonboom/errname v0.1.10 // indirect + github.com/Antonboom/nilnil v0.1.5 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest v0.11.18 // indirect @@ -47,13 +48,14 @@ require ( github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect - github.com/BurntSushi/toml v1.2.1 // indirect + github.com/BurntSushi/toml v1.3.0 // indirect github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 // indirect github.com/GaijinEntertainment/go-exhaustruct/v2 v2.3.0 // indirect github.com/Masterminds/semver v1.5.0 // indirect github.com/NYTimes/gziphandler v1.1.1 // indirect - github.com/OpenPeeDeeP/depguard v1.1.1 // indirect + github.com/OpenPeeDeeP/depguard/v2 v2.0.1 // indirect github.com/PuerkitoBio/purell v1.2.0 // indirect + github.com/alexkohler/nakedret/v2 v2.0.1 // indirect github.com/alexkohler/prealloc v1.0.0 // indirect github.com/alingse/asasalint v0.0.11 // indirect github.com/ashanbrown/forbidigo v1.5.1 // indirect @@ -66,7 +68,8 @@ require ( github.com/bombsimon/wsl/v3 v3.4.0 // indirect github.com/breml/bidichk v0.2.4 // indirect github.com/breml/errchkjson v0.3.1 // indirect - github.com/butuzov/ireturn v0.1.1 // indirect + github.com/butuzov/ireturn v0.2.0 // indirect + github.com/butuzov/mirror v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/charithe/durationcheck v0.0.10 // indirect github.com/chavacava/garif v0.0.0-20230227094218-b8c73b2037b8 // indirect @@ -91,7 +94,7 @@ require ( github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect github.com/fzipp/gocyclo v0.6.0 // indirect - github.com/go-critic/go-critic v0.7.0 // indirect + github.com/go-critic/go-critic v0.8.1 // indirect github.com/go-errors/errors v1.0.1 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/zapr v1.2.0 // indirect @@ -149,15 +152,14 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/julz/importas v0.1.0 // indirect - github.com/junk1tm/musttag v0.5.0 // indirect github.com/kisielk/errcheck v1.6.3 // indirect github.com/kisielk/gotool v1.0.0 // indirect github.com/kkHAIKE/contextcheck v1.1.4 // indirect github.com/kulti/thelper v0.6.3 // indirect - github.com/kunwardeep/paralleltest v1.0.6 // indirect + github.com/kunwardeep/paralleltest v1.0.7 // indirect github.com/kyoh86/exportloopref v0.1.11 // indirect github.com/ldez/gomoddirectives v0.2.3 // indirect - github.com/ldez/tagliatelle v0.4.0 // indirect + github.com/ldez/tagliatelle v0.5.0 // indirect github.com/leonklingele/grouper v1.1.1 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/lufeee/execinquery v1.2.1 // indirect @@ -171,7 +173,7 @@ require ( github.com/mattn/go-runewidth v0.0.9 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect github.com/mbilski/exhaustivestruct v1.2.0 // indirect - github.com/mgechev/revive v1.3.1 // indirect + github.com/mgechev/revive v1.3.2 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/moby/term v0.0.0-20221205130635-1aeaba878587 // indirect @@ -182,9 +184,9 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/nakabonne/nestif v0.3.1 // indirect github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 // indirect - github.com/nishanths/exhaustive v0.9.5 // indirect + github.com/nishanths/exhaustive v0.10.0 // indirect github.com/nishanths/predeclared v0.2.2 // indirect - github.com/nunnatsa/ginkgolinter v0.9.0 // indirect + github.com/nunnatsa/ginkgolinter v0.12.0 // indirect github.com/nxadm/tail v1.4.8 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect @@ -195,7 +197,7 @@ require ( github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/polyfloyd/go-errorlint v1.4.0 // indirect + github.com/polyfloyd/go-errorlint v1.4.2 // 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 @@ -209,10 +211,10 @@ require ( github.com/sanposhiho/wastedassign/v2 v2.0.7 // indirect github.com/sashamelentyev/interfacebloat v1.1.0 // indirect github.com/sashamelentyev/usestdlibvars v1.23.0 // indirect - github.com/securego/gosec/v2 v2.15.0 // indirect + github.com/securego/gosec/v2 v2.16.0 // indirect github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c // indirect - github.com/sirupsen/logrus v1.9.0 // indirect - github.com/sivchari/containedctx v1.0.2 // indirect + github.com/sirupsen/logrus v1.9.2 // indirect + github.com/sivchari/containedctx v1.0.3 // indirect github.com/sivchari/nosnakecase v1.7.0 // indirect github.com/sivchari/tenv v1.7.1 // indirect github.com/sonatard/noctx v0.0.2 // indirect @@ -224,21 +226,23 @@ require ( github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect github.com/stbenjam/no-sprintf-host-port v0.1.1 // indirect github.com/stretchr/objx v0.5.0 // indirect - github.com/stretchr/testify v1.8.2 // indirect + github.com/stretchr/testify v1.8.4 // 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.11 // indirect - github.com/timakin/bodyclose v0.0.0-20221125081123-e39cf3fc478e // 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.1 // indirect github.com/tommy-muehle/go-mnd/v2 v2.5.1 // indirect github.com/ultraware/funlen v0.0.3 // indirect github.com/ultraware/whitespace v0.0.5 // indirect github.com/uudashr/gocognit v1.0.6 // indirect + github.com/xen0n/gosmopolitan v1.2.1 // indirect github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca // indirect github.com/yagipy/maintidx v1.0.0 // indirect github.com/yeya24/promlinter v0.2.0 // indirect + github.com/ykadowak/zerologlint v0.1.1 // indirect gitlab.com/bosi/decorder v0.2.3 // indirect go.etcd.io/etcd/api/v3 v3.5.7 // indirect go.etcd.io/etcd/client/pkg/v3 v3.5.7 // indirect @@ -255,11 +259,12 @@ require ( go.opentelemetry.io/otel/trace v0.20.0 // indirect go.opentelemetry.io/proto/otlp v0.7.0 // indirect go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 // indirect + go.tmz.dev/musttag v0.7.0 // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.24.0 // indirect - golang.org/x/crypto v0.6.0 // indirect - golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 // indirect + golang.org/x/crypto v0.9.0 // indirect + golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea // indirect golang.org/x/exp/typeparams v0.0.0-20230224173230-c95f2b4c22f2 // indirect golang.org/x/mod v0.10.0 // indirect golang.org/x/net v0.10.0 // indirect @@ -269,7 +274,7 @@ require ( golang.org/x/term v0.8.0 // indirect golang.org/x/text v0.9.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.9.1 // indirect + golang.org/x/tools v0.9.2 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect @@ -288,7 +293,7 @@ require ( k8s.io/kube-scheduler v0.23.10 // indirect k8s.io/mount-utils v0.23.10 // indirect k8s.io/utils v0.0.0-20211208161948-7d6a63dca704 // indirect - mvdan.cc/gofumpt v0.4.0 // indirect + mvdan.cc/gofumpt v0.5.0 // indirect mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed // indirect mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b // indirect mvdan.cc/unparam v0.0.0-20221223090309-7455f1af531d // indirect diff --git a/go.sum b/go.sum index ec12ae66..e3bac48f 100644 --- a/go.sum +++ b/go.sum @@ -51,12 +51,14 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20201218220906-28db891af037/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/4meepo/tagalign v1.2.2 h1:kQeUTkFTaBRtd/7jm8OKJl9iHk0gAO+TDFPHGSna0aw= +github.com/4meepo/tagalign v1.2.2/go.mod h1:Q9c1rYMZJc9dPRkbQPpcBNCLEmY2njbAsXhQOZFE2dE= github.com/Abirdcfly/dupword v0.0.11 h1:z6v8rMETchZXUIuHxYNmlUAuKuB21PeaSymTed16wgU= github.com/Abirdcfly/dupword v0.0.11/go.mod h1:wH8mVGuf3CP5fsBTkfWwwwKTjDnVVCxtU8d8rgeVYXA= -github.com/Antonboom/errname v0.1.9 h1:BZDX4r3l4TBZxZ2o2LNrlGxSHran4d1u4veZdoORTT4= -github.com/Antonboom/errname v0.1.9/go.mod h1:nLTcJzevREuAsgTbG85UsuiWpMpAqbKD1HNZ29OzE58= -github.com/Antonboom/nilnil v0.1.3 h1:6RTbx3d2mcEu3Zwq9TowQpQMVpP75zugwOtqY1RTtcE= -github.com/Antonboom/nilnil v0.1.3/go.mod h1:iOov/7gRcXkeEU+EMGpBu2ORih3iyVEiWjeste1SJm8= +github.com/Antonboom/errname v0.1.10 h1:RZ7cYo/GuZqjr1nuJLNe8ZH+a+Jd9DaZzttWzak9Bls= +github.com/Antonboom/errname v0.1.10/go.mod h1:xLeiCIrvVNpUtsN0wxAh05bNIZpqE22/qDMnTBTttiA= +github.com/Antonboom/nilnil v0.1.5 h1:X2JAdEVcbPaOom2TUa1FxZ3uyuUlex0XMLGYMemu6l0= +github.com/Antonboom/nilnil v0.1.5/go.mod h1:I24toVuBKhfP5teihGWctrRiPbRKHwZIFOvc6v3HZXk= github.com/Azure/azure-sdk-for-go v55.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= @@ -80,8 +82,8 @@ github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZ github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= -github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.3.0 h1:Ws8e5YmnrGEHzZEzg0YvK/7COGYtTC5PbaH9oSSbgfA= +github.com/BurntSushi/toml v1.3.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 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= @@ -101,8 +103,8 @@ github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb0 github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/OpenPeeDeeP/depguard v1.1.1 h1:TSUznLjvp/4IUP+OQ0t/4jF4QUyxIcVX8YnghZdunyA= -github.com/OpenPeeDeeP/depguard v1.1.1/go.mod h1:JtAMzWkmFEzDPyAd+W0NHl1lvpQKTvT9jnRVsohBKpc= +github.com/OpenPeeDeeP/depguard/v2 v2.0.1 h1:yr9ZswukmNxl/hmJHEoLEjCF1d+f2pQrC0m1jzVljAE= +github.com/OpenPeeDeeP/depguard/v2 v2.0.1/go.mod h1:gwSk4XDpowOuQSsMWNK5F7+C3kMz7QIexKRkD/GL4GU= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.2.0 h1:/Jdm5QfyM8zdlqT6WVZU4cfP23sot6CEHA4CS49Ezig= github.com/PuerkitoBio/purell v1.2.0/go.mod h1:OhLRTaaIzhvIyofkJfB24gokC7tM42Px5UhoT32THBk= @@ -113,6 +115,8 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/alexkohler/nakedret/v2 v2.0.1 h1:DLFVWaHbEntNHBYGhPX+AhCM1gCErTs35IFWPh6Bnn0= +github.com/alexkohler/nakedret/v2 v2.0.1/go.mod h1:2b8Gkk0GsOrqQv/gPWjNLDSKwG8I5moSXG1K4VIBcTQ= github.com/alexkohler/prealloc v1.0.0 h1:Hbq0/3fJPQhNkN0dR95AVrr6R7tou91y0uHG5pOcUuw= github.com/alexkohler/prealloc v1.0.0/go.mod h1:VetnK3dIgFBBKmg0YnD9F9x6Icjd+9cvfHR56wJVlKE= github.com/alingse/asasalint v0.0.11 h1:SFwnQXJ49Kx/1GghOFz1XGqHYKp21Kq1nHad/0WQRnw= @@ -157,8 +161,10 @@ github.com/breml/bidichk v0.2.4 h1:i3yedFWWQ7YzjdZJHnPo9d/xURinSq3OM+gyM43K4/8= github.com/breml/bidichk v0.2.4/go.mod h1:7Zk0kRFt1LIZxtQdl9W9JwGAcLTTkOs+tN7wuEYGJ3s= github.com/breml/errchkjson v0.3.1 h1:hlIeXuspTyt8Y/UmP5qy1JocGNR00KQHgfaNtRAjoxQ= github.com/breml/errchkjson v0.3.1/go.mod h1:XroxrzKjdiutFyW3nWhw34VGg7kiMsDQox73yWCGI2U= -github.com/butuzov/ireturn v0.1.1 h1:QvrO2QF2+/Cx1WA/vETCIYBKtRjc30vesdoPUNo1EbY= -github.com/butuzov/ireturn v0.1.1/go.mod h1:Wh6Zl3IMtTpaIKbmwzqi6olnM9ptYQxxVacMsOEFPoc= +github.com/butuzov/ireturn v0.2.0 h1:kCHi+YzC150GE98WFuZQu9yrTn6GEydO2AuPLbTgnO4= +github.com/butuzov/ireturn v0.2.0/go.mod h1:Wh6Zl3IMtTpaIKbmwzqi6olnM9ptYQxxVacMsOEFPoc= +github.com/butuzov/mirror v1.1.0 h1:ZqX54gBVMXu78QLoiqdwpl2mgmoOJTk7s4p4o+0avZI= +github.com/butuzov/mirror v1.1.0/go.mod h1:8Q0BdQU6rC6WILDiBM60DBfvV78OLJmMmixe7GF45AE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= @@ -294,7 +300,7 @@ github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoD github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= -github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= @@ -305,8 +311,8 @@ github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlya github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-critic/go-critic v0.7.0 h1:tqbKzB8pqi0NsRZ+1pyU4aweAF7A7QN0Pi4Q02+rYnQ= -github.com/go-critic/go-critic v0.7.0/go.mod h1:moYzd7GdVXE2C2hYTwd7h0CPcqlUeclsyBRwMa38v64= +github.com/go-critic/go-critic v0.8.1 h1:16omCF1gN3gTzt4j4J6fKI/HnRojhEp+Eks6EuKw3vw= +github.com/go-critic/go-critic v0.8.1/go.mod h1:kpzXl09SIJX1cr9TB/g/sAG+eFEl7ZS9f9cqvZtyNl0= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -418,8 +424,8 @@ github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe h1:6RGUuS7EGotKx6 github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe/go.mod h1:gjqyPShc/m8pEMpk0a3SeagVb0kaqvhscv+i9jI5ZhQ= github.com/golangci/gofmt v0.0.0-20220901101216-f2edd75033f2 h1:amWTbTGqOZ71ruzrdA+Nx5WA3tV1N0goTspwmKCQvBY= github.com/golangci/gofmt v0.0.0-20220901101216-f2edd75033f2/go.mod h1:9wOXstvyDRshQ9LggQuzBCGysxs3b6Uo/1MvYCR2NMs= -github.com/golangci/golangci-lint v1.52.2 h1:FrPElUUI5rrHXg1mQ7KxI1MXPAw5lBVskiz7U7a8a1A= -github.com/golangci/golangci-lint v1.52.2/go.mod h1:S5fhC5sHM5kE22/HcATKd1XLWQxX+y7mHj8B5H91Q/0= +github.com/golangci/golangci-lint v1.53.0 h1:UigoFKBXwmoJVNWeht27A0jnuZjdOIV4lvMYuA9naj4= +github.com/golangci/golangci-lint v1.53.0/go.mod h1:Cwv11QjWoiq/UXE4pL3LInd7SNwMWV8S5Obur2pA5Ug= github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 h1:MfyDlzVjl1hoaPzPD4Gpb/QgoRfSBR0jdhwGyAWwMSA= github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg= github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca h1:kNY3/svz5T29MYHubXix4aDDuE3RWHkPvopM/EDv/MA= @@ -587,8 +593,6 @@ github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8 github.com/julz/importas v0.1.0 h1:F78HnrsjY3cR7j0etXy5+TU1Zuy7Xt08X/1aJnH5xXY= github.com/julz/importas v0.1.0/go.mod h1:oSFU2R4XK/P7kNBrnL/FEQlDGN1/6WoxXEjSSXO0DV0= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/junk1tm/musttag v0.5.0 h1:bV1DTdi38Hi4pG4OVWa7Kap0hi0o7EczuK6wQt9zPOM= -github.com/junk1tm/musttag v0.5.0/go.mod h1:PcR7BA+oREQYvHwgjIDmw3exJeds5JzRcvEJTfjrA0M= github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= @@ -607,22 +611,22 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kulti/thelper v0.6.3 h1:ElhKf+AlItIu+xGnI990no4cE2+XaSu1ULymV2Yulxs= github.com/kulti/thelper v0.6.3/go.mod h1:DsqKShOvP40epevkFrvIwkCMNYxMeTNjdWL4dqWHZ6I= -github.com/kunwardeep/paralleltest v1.0.6 h1:FCKYMF1OF2+RveWlABsdnmsvJrei5aoyZoaGS+Ugg8g= -github.com/kunwardeep/paralleltest v1.0.6/go.mod h1:Y0Y0XISdZM5IKm3TREQMZ6iteqn1YuwCsJO/0kL9Zes= +github.com/kunwardeep/paralleltest v1.0.7 h1:2uCk94js0+nVNQoHZNLBkAR1DQJrVzw6T0RMzJn55dQ= +github.com/kunwardeep/paralleltest v1.0.7/go.mod h1:2C7s65hONVqY7Q5Efj5aLzRCNLjw2h4eMc9EcypGjcY= github.com/kyoh86/exportloopref v0.1.11 h1:1Z0bcmTypkL3Q4k+IDHMWTcnCliEZcaPiIe0/ymEyhQ= github.com/kyoh86/exportloopref v0.1.11/go.mod h1:qkV4UF1zGl6EkF1ox8L5t9SwyeBAZ3qLMd6up458uqA= github.com/ldez/gomoddirectives v0.2.3 h1:y7MBaisZVDYmKvt9/l1mjNCiSA1BVn34U0ObUcJwlhA= github.com/ldez/gomoddirectives v0.2.3/go.mod h1:cpgBogWITnCfRq2qGoDkKMEVSaarhdBr6g8G04uz6d0= -github.com/ldez/tagliatelle v0.4.0 h1:sylp7d9kh6AdXN2DpVGHBRb5guTVAgOxqNGhbqc4b1c= -github.com/ldez/tagliatelle v0.4.0/go.mod h1:mNtTfrHy2haaBAw+VT7IBV6VXBThS7TCreYWbBcJ87I= +github.com/ldez/tagliatelle v0.5.0 h1:epgfuYt9v0CG3fms0pEgIMNPuFf/LpPIfjk4kyqSioo= +github.com/ldez/tagliatelle v0.5.0/go.mod h1:rj1HmWiL1MiKQuOONhd09iySTEkUuE/8+5jtPYz9xa4= github.com/leonklingele/grouper v1.1.1 h1:suWXRU57D4/Enn6pXR0QVqqWWrnJ9Osrz+5rjt8ivzU= github.com/leonklingele/grouper v1.1.1/go.mod h1:uk3I3uDfi9B6PeUjsCKi6ndcf63Uy7snXgR4yDYQVDY= github.com/libopenstorage/openstorage v1.0.0/go.mod h1:Sp1sIObHjat1BeXhfMqLZ14wnOzEhNx2YQedreMcUyc= @@ -665,8 +669,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182aff github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mbilski/exhaustivestruct v1.2.0 h1:wCBmUnSYufAHO6J4AVWY6ff+oxWxsVFrwgOdMUQePUo= github.com/mbilski/exhaustivestruct v1.2.0/go.mod h1:OeTBVxQWoEmB2J2JCHmXWPJ0aksxSUOUy+nvtVEfzXc= -github.com/mgechev/revive v1.3.1 h1:OlQkcH40IB2cGuprTPcjB0iIUddgVZgGmDX3IAMR8D4= -github.com/mgechev/revive v1.3.1/go.mod h1:YlD6TTWl2B8A103R9KWJSPVI9DrEf+oqr15q21Ld+5I= +github.com/mgechev/revive v1.3.2 h1:Wb8NQKBaALBJ3xrrj4zpwJwqwNA6nDpyJSEQWcCka6U= +github.com/mgechev/revive v1.3.2/go.mod h1:UCLtc7o5vg5aXCwdUTU1kEBQ1v+YXPAkYDIDXbrs5I0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mindprince/gonvml v0.0.0-20190828220739-9ebdce4bb989/go.mod h1:2eu9pRWp8mo84xCg6KswZ+USQHjwgRhNp06sozOdsTY= github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= @@ -716,12 +720,12 @@ github.com/nakabonne/nestif v0.3.1/go.mod h1:9EtoZochLn5iUprVDmDjqGKPofoUEBL8U4N github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 h1:4kuARK6Y6FxaNu/BnU2OAaLF86eTVhP2hjTB6iMvItA= github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nishanths/exhaustive v0.9.5 h1:TzssWan6orBiLYVqewCG8faud9qlFntJE30ACpzmGME= -github.com/nishanths/exhaustive v0.9.5/go.mod h1:IbwrGdVMizvDcIxPYGVdQn5BqWJaOwpCvg4RGb8r/TA= +github.com/nishanths/exhaustive v0.10.0 h1:BMznKAcVa9WOoLq/kTGp4NJOJSMwEpcpjFNAVRfPlSo= +github.com/nishanths/exhaustive v0.10.0/go.mod h1:IbwrGdVMizvDcIxPYGVdQn5BqWJaOwpCvg4RGb8r/TA= github.com/nishanths/predeclared v0.2.2 h1:V2EPdZPliZymNAn79T8RkNApBjMmVKh5XRpLm/w98Vk= github.com/nishanths/predeclared v0.2.2/go.mod h1:RROzoN6TnGQupbC+lqggsOlcgysk3LMK/HI84Mp280c= -github.com/nunnatsa/ginkgolinter v0.9.0 h1:Sm0zX5QfjJzkeCjEp+t6d3Ha0jwvoDjleP9XCsrEzOA= -github.com/nunnatsa/ginkgolinter v0.9.0/go.mod h1:FHaMLURXP7qImeH6bvxWJUpyH+2tuqe5j4rW1gxJRmI= +github.com/nunnatsa/ginkgolinter v0.12.0 h1:seZo112n+lt0gdLJ/Jh70mzvrqbABWFpXd1bZTLTByM= +github.com/nunnatsa/ginkgolinter v0.12.0/go.mod h1:dJIGXYXbkBswqa/pIzG0QlVTTDSBMxDoCFwhsl4Uras= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= @@ -778,8 +782,8 @@ github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZ github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= 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.4.0 h1:b+sQ5HibPIAjEZwtuwU8Wz/u0dMZ7YL+bk+9yWyHVJk= -github.com/polyfloyd/go-errorlint v1.4.0/go.mod h1:qJCkPeBn+0EXkdKTrUCcuFStM2xrDKfxI3MGLXPexUs= +github.com/polyfloyd/go-errorlint v1.4.2 h1:CU+O4181IxFDdPH6t/HT7IiDj1I7zxNi1RIUxYwn8d0= +github.com/polyfloyd/go-errorlint v1.4.2/go.mod h1:k6fU/+fQe38ednoZS51T7gSIGQW1y94d6TkSr35OzH8= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -826,7 +830,7 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rubiojr/go-vhd v0.0.0-20200706105327-02e210299021/go.mod h1:DM5xW0nvfNNm2uytzsvhI3OnX8uzaRAg8UX/CnDqbto= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -844,8 +848,8 @@ github.com/sashamelentyev/usestdlibvars v1.23.0 h1:01h+/2Kd+NblNItNeux0veSL5cBF1 github.com/sashamelentyev/usestdlibvars v1.23.0/go.mod h1:YPwr/Y1LATzHI93CqoPUN/2BzGQ/6N/cl/KwgR0B/aU= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= -github.com/securego/gosec/v2 v2.15.0 h1:v4Ym7FF58/jlykYmmhZ7mTm7FQvN/setNm++0fgIAtw= -github.com/securego/gosec/v2 v2.15.0/go.mod h1:VOjTrZOkUtSDt2QLSJmQBMWnvwiQPEjg0l+5juIqGk8= +github.com/securego/gosec/v2 v2.16.0 h1:Pi0JKoasQQ3NnoRao/ww/N/XdynIB9NRYYZT5CyOs5U= +github.com/securego/gosec/v2 v2.16.0/go.mod h1:xvLcVZqUfo4aAQu56TNv7/Ltz6emAOQAEsrZrt7uGlI= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c h1:W65qqJCIOVP4jpqPQ0YvHYKwcMEMVWIzWC5iNQQfBTU= @@ -859,10 +863,10 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/sivchari/containedctx v1.0.2 h1:0hLQKpgC53OVF1VT7CeoFHk9YKstur1XOgfYIc1yrHI= -github.com/sivchari/containedctx v1.0.2/go.mod h1:PwZOeqm4/DLoJOqMSIJs3aKqXRX4YO+uXww087KZ7Bw= +github.com/sirupsen/logrus v1.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y= +github.com/sirupsen/logrus v1.9.2/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/nosnakecase v1.7.0 h1:7QkpWIRMe8x25gckkFd2A5Pi6Ymo0qgr4JrhGt95do8= github.com/sivchari/nosnakecase v1.7.0/go.mod h1:CwDzrzPea40/GB6uynrNLiorAlgFRvRbFSgJx2Gs+QY= github.com/sivchari/tenv v1.7.1 h1:PSpuD4bu6fSmtWMxSGWcvqUUgIn7k3yOJhOIzVWn8Ak= @@ -925,8 +929,9 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= @@ -941,8 +946,8 @@ github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3 h1:f+jULpR github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY= github.com/tetafro/godot v1.4.11 h1:BVoBIqAf/2QdbFmSwAWnaIqDivZdOV0ZRwEm6jivLKw= github.com/tetafro/godot v1.4.11/go.mod h1:LR3CJpxDVGlYOWn3ZZg1PgNZdTUvzsZWu8xaEohUpn8= -github.com/timakin/bodyclose v0.0.0-20221125081123-e39cf3fc478e h1:MV6KaVu/hzByHP0UvJ4HcMGE/8a6A4Rggc/0wx2AvJo= -github.com/timakin/bodyclose v0.0.0-20221125081123-e39cf3fc478e/go.mod h1:27bSVNWSBOHm+qRp1T9qzaIpsWEP6TbUnei/43HK+PQ= +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/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -966,6 +971,8 @@ github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYp github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/vmware/govmomi v0.20.3/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU= +github.com/xen0n/gosmopolitan v1.2.1 h1:3pttnTuFumELBRSh+KQs1zcz4fN6Zy7aB0xlnQSn1Iw= +github.com/xen0n/gosmopolitan v1.2.1/go.mod h1:JsHq/Brs1o050OOdmzHeOr0N7OtlnKRAGAsElF8xBQA= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca h1:1CFlNzQhALwjS9mBAUkycX616GzgsuYUOCHA5+HSlXI= @@ -975,6 +982,8 @@ github.com/yagipy/maintidx v1.0.0 h1:h5NvIsCz+nRDapQ0exNv4aJ0yXSI0420omVANTv3GJM github.com/yagipy/maintidx v1.0.0/go.mod h1:0qNf/I/CCZXSMhsRsrEPDZ+DkekpKLXAJfsTACwgXLk= github.com/yeya24/promlinter v0.2.0 h1:xFKDQ82orCU5jQujdaD8stOHiv8UN68BSdn2a8u8Y3o= github.com/yeya24/promlinter v0.2.0/go.mod h1:u54lkmBOZrpEbQQ6gox2zWKKLKu2SGe+2KOiextY+IA= +github.com/ykadowak/zerologlint v0.1.1 h1:CA1+RsGS1DbBn3jJP2jpWfiMJipWdeqJfSY0GpNgqaY= +github.com/ykadowak/zerologlint v0.1.1/go.mod h1:KaUskqF3e/v59oPmdq1U1DnKcuHokl2/K1U4pmIELKg= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -985,6 +994,7 @@ github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= gitlab.com/bosi/decorder v0.2.3 h1:gX4/RgK16ijY8V+BRQHAySfQAb354T7/xQpDB2n10P0= gitlab.com/bosi/decorder v0.2.3/go.mod h1:9K1RB5+VPNQYtXtTDAzd2OEftsZb1oV0IrJrzChSdGE= +go-simpler.org/assert v0.5.0 h1:+5L/lajuQtzmbtEfh69sr5cRf2/xZzyJhFjoOz/PPqs= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= @@ -1038,6 +1048,8 @@ go.opentelemetry.io/proto/otlp v0.7.0 h1:rwOQPCuKAKmwGKq2aVNnYIibI6wnV7EvzgfTCzc go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5 h1:+FNtrFTmVw0YZGpBGX56XDee331t6JAXeK2bcyhLOOc= go.starlark.net v0.0.0-20200306205701-8dd3e2ee1dd5/go.mod h1:nmDLcffg48OtT/PSW0Hg7FvpRQsQh5OSqIylirxKC7o= +go.tmz.dev/musttag v0.7.0 h1:QfytzjTWGXZmChoX0L++7uQN+yRCPfyFm+whsM+lfGc= +go.tmz.dev/musttag v0.7.0/go.mod h1:oTFPvgOkJmp5kYL02S8+jrH0eLrBIl57rzWeA26zDEM= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= @@ -1066,8 +1078,8 @@ golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= -golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= -golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1084,8 +1096,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 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-20210220032938-85be41e4509f/go.mod h1:I6l2HNBLBZEcrOoCpyKLdY2lHoRZ8lI4x60KMCQDft4= -golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 h1:Jvc7gsqn21cJHCmAWx0LiimpP18LZmUxkT5Mp7EZ1mI= -golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea h1:vLCWI/yYrdEHyN2JzIzPO3aaQJHQdp89IZBA/+azVC4= +golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= 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-20230224173230-c95f2b4c22f2 h1:J74nGeMgeFnYQJN59eFwh06jX/V8g0lB7LWpjSLxtgU= @@ -1429,8 +1441,8 @@ golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= -golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= +golang.org/x/tools v0.9.2 h1:UXbndbirwCAx6TULftIfie/ygDNCwxEie+IiNP1IcNc= +golang.org/x/tools v0.9.2/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1680,8 +1692,8 @@ modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= -mvdan.cc/gofumpt v0.4.0 h1:JVf4NN1mIpHogBj7ABpgOyZc65/UUOkKQFkoURsz4MM= -mvdan.cc/gofumpt v0.4.0/go.mod h1:PljLOHDeZqgS8opHRKLzp2It2VBuSdteAgqUfzMTxlQ= +mvdan.cc/gofumpt v0.5.0 h1:0EQ+Z56k8tXjj/6TQD25BFNKQXpCvT0rnansIc7Ug5E= +mvdan.cc/gofumpt v0.5.0/go.mod h1:HBeVDtMKRZpXyxFciAirzdKklDlGu8aAy1wEbH5Y9js= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo= diff --git a/vendor/github.com/4meepo/tagalign/.gitignore b/vendor/github.com/4meepo/tagalign/.gitignore new file mode 100644 index 00000000..e37bb52e --- /dev/null +++ b/vendor/github.com/4meepo/tagalign/.gitignore @@ -0,0 +1,75 @@ +# File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig +# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,macos,go +# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,macos,go + +### Go ### +# If you prefer the allow list template instead of the deny list, see community template: +# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore +# +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +.vscode + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Go workspace file +go.work + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,macos,go + +# Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) diff --git a/vendor/github.com/4meepo/tagalign/.golangci.yml b/vendor/github.com/4meepo/tagalign/.golangci.yml new file mode 100644 index 00000000..99baa8c2 --- /dev/null +++ b/vendor/github.com/4meepo/tagalign/.golangci.yml @@ -0,0 +1,107 @@ +# See https://golangci-lint.run/usage/configuration/ + +linters-settings: + revive: + # see https://github.com/mgechev/revive#available-rules for details. + ignore-generated-header: true + severity: warning + rules: + - name: atomic + - name: blank-imports + - name: bool-literal-in-expr + - name: call-to-gc + - name: confusing-naming + - name: confusing-results + - name: constant-logical-expr + - name: context-as-argument + - name: context-keys-type + - name: deep-exit + - name: defer + - name: dot-imports + - name: duplicated-imports + - name: early-return + - name: empty-block + - name: empty-lines + - name: error-naming + - name: error-return + - name: error-strings + - name: errorf + - name: exported + - name: get-return + - name: identical-branches + - name: if-return + - name: import-shadowing + - name: increment-decrement + - name: indent-error-flow + - name: modifies-parameter + - name: modifies-value-receiver + - name: package-comments + - name: range + - name: range-val-address + - name: range-val-in-closure + - name: receiver-naming + - name: redefines-builtin-id + - name: string-of-int + - name: struct-tag + - name: superfluous-else + - name: time-naming + - name: unconditional-recursion + - name: unexported-naming + - name: unexported-return + - name: unnecessary-stmt + - name: unreachable-code + - name: unused-parameter + - name: var-declaration + - name: var-naming + - name: waitgroup-by-value + +linters: + disable-all: true + enable: + - asciicheck + - bodyclose + - dogsled + - dupl + - durationcheck + - errcheck + - errorlint + - exhaustive + - exportloopref + - forcetypeassert + - gochecknoinits + - gocognit + - goconst + - gocritic + - gocyclo + - godot + - godox + - goimports + - gomoddirectives + - gomodguard + - goprintffuncname + - gosec + - gosimple + - govet + - importas + - ineffassign + - makezero + - misspell + - nakedret + - nestif + - nilerr + - noctx + - nolintlint + - prealloc + - predeclared + - revive + - rowserrcheck + - sqlclosecheck + - staticcheck + - stylecheck + - thelper + - tparallel + - typecheck + - unconvert + - unparam + - unused + - whitespace diff --git a/vendor/github.com/4meepo/tagalign/.goreleaser.yml b/vendor/github.com/4meepo/tagalign/.goreleaser.yml new file mode 100644 index 00000000..e7b6f680 --- /dev/null +++ b/vendor/github.com/4meepo/tagalign/.goreleaser.yml @@ -0,0 +1,32 @@ +--- +project_name: tagalign + +release: + github: + owner: 4meepo + name: tagalign + +builds: + - binary: tagalign + goos: + - darwin + - windows + - linux + - freebsd + goarch: + - amd64 + - arm64 + - arm + goarm: + - 6 + - 7 + gomips: + - hardfloat + env: + - CGO_ENABLED=0 + ignore: + - goos: darwin + goarch: 386 + - goos: freebsd + goarch: arm64 + main: ./cmd/tagalign/ \ No newline at end of file diff --git a/vendor/github.com/junk1tm/musttag/LICENSE b/vendor/github.com/4meepo/tagalign/LICENSE similarity index 97% rename from vendor/github.com/junk1tm/musttag/LICENSE rename to vendor/github.com/4meepo/tagalign/LICENSE index 38baef8d..da3ae827 100644 --- a/vendor/github.com/junk1tm/musttag/LICENSE +++ b/vendor/github.com/4meepo/tagalign/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022 junk1tm +Copyright (c) 2023 Yifei Liu Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/vendor/github.com/4meepo/tagalign/Makefile b/vendor/github.com/4meepo/tagalign/Makefile new file mode 100644 index 00000000..614e7773 --- /dev/null +++ b/vendor/github.com/4meepo/tagalign/Makefile @@ -0,0 +1,7 @@ +.PHONY: lint +lint: + golangci-lint run ./... + +.PHONY: build +build: + go build -o tagalign cmd/tagalign/tagalign.go \ No newline at end of file diff --git a/vendor/github.com/4meepo/tagalign/README.md b/vendor/github.com/4meepo/tagalign/README.md new file mode 100644 index 00000000..9181e7a1 --- /dev/null +++ b/vendor/github.com/4meepo/tagalign/README.md @@ -0,0 +1,99 @@ +# Go Tag Align + +![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/4meepo/tagalign?style=flat-square) +[![codecov](https://codecov.io/github/4meepo/tagalign/branch/main/graph/badge.svg?token=1R1T61UNBQ)](https://codecov.io/github/4meepo/tagalign) +[![GoDoc](https://godoc.org/github.com/4meepo/tagalign?status.svg)](https://pkg.go.dev/github.com/4meepo/tagalign) +[![Go Report Card](https://goreportcard.com/badge/github.com/4meepo/tagalign)](https://goreportcard.com/report/github.com/4meepo/tagalign) + +TagAlign is used to align and sort tags in Go struct. It can make the struct more readable and easier to maintain. + +For example, this struct + +```go +type FooBar struct { + Foo int `json:"foo" validate:"required"` + Bar string `json:"bar" validate:"required"` + FooFoo int8 `json:"foo_foo" validate:"required"` + BarBar int `json:"bar_bar" validate:"required"` + FooBar struct { + Foo int `json:"foo" yaml:"foo" validate:"required"` + Bar222 string `json:"bar222" validate:"required" yaml:"bar"` + } `json:"foo_bar" validate:"required"` + BarFoo string `json:"bar_foo" validate:"required"` + BarFooBar string `json:"bar_foo_bar" validate:"required"` +} +``` + +can be aligned to: + +```go +type FooBar struct { + Foo int `json:"foo" validate:"required"` + Bar string `json:"bar" validate:"required"` + FooFoo int8 `json:"foo_foo" validate:"required"` + BarBar int `json:"bar_bar" validate:"required"` + FooBar struct { + Foo int `json:"foo" yaml:"foo" validate:"required"` + Bar222 string `json:"bar222" validate:"required" yaml:"bar"` + } `json:"foo_bar" validate:"required"` + BarFoo string `json:"bar_foo" validate:"required"` + BarFooBar string `json:"bar_foo_bar" validate:"required"` +} +``` + +In addition to alignment, it can also sort tags with fixed order. If we enable sort with fixed order `json,xml`, the following code + +```go +type SortExample struct { + Foo int `json:"foo,omitempty" yaml:"bar" xml:"baz" binding:"required" gorm:"column:foo" zip:"foo" validate:"required"` + Bar int `validate:"required" yaml:"foo" xml:"bar" binding:"required" json:"bar,omitempty" gorm:"column:bar" zip:"bar" ` + FooBar int `gorm:"column:bar" validate:"required" xml:"bar" binding:"required" json:"bar,omitempty" zip:"bar" yaml:"foo"` +} +``` + +will be sorted and aligned to: + +```go +type SortExample struct { + Foo int `json:"foo,omitempty" xml:"baz" binding:"required" gorm:"column:foo" validate:"required" yaml:"bar" zip:"foo"` + Bar int `json:"bar,omitempty" xml:"bar" binding:"required" gorm:"column:bar" validate:"required" yaml:"foo" zip:"bar"` + FooBar int `json:"bar,omitempty" xml:"bar" binding:"required" gorm:"column:bar" validate:"required" yaml:"foo" zip:"bar"` +} +``` + +The fixed order is `json,xml`, so the tags `json` and `xml` will be sorted and aligned first, and the rest tags will be sorted and aligned in the dictionary order. + +## Install + +```bash +go install github.com/4meepo/tagalign/cmd/tagalign +``` + +## Usage + +By default tagalign will only align tags, but not sort them. But alignment and sort can work together or separately. + +If you don't want to align tags, you can use `-noalign` to disable alignment. + +You can use `-sort` to enable sort and `-order` to set the fixed order of tags. + +```bash +# Only align tags. +tagalign -fix {package path} +# Only sort tags with fixed order. +tagalign -fix -noalign -sort -order "json,xml" {package path} +# Align and sort together. +tagalign -fix -sort -order "json,xml" {package path} +``` + +TODO: integrate with golangci-lint + +## Reference + +[Golang AST Visualizer](http://goast.yuroyoro.net/) + +[Create New Golang CI Linter](https://golangci-lint.run/contributing/new-linters/) + +[Autofix Example](https://github.com/golangci/golangci-lint/pull/2450/files) + +[Integrating](https://disaev.me/p/writing-useful-go-analysis-linter/#integrating) diff --git a/vendor/github.com/4meepo/tagalign/options.go b/vendor/github.com/4meepo/tagalign/options.go new file mode 100644 index 00000000..4deaf8cb --- /dev/null +++ b/vendor/github.com/4meepo/tagalign/options.go @@ -0,0 +1,28 @@ +package tagalign + +type Option func(*Helper) + +// WithMode specify the mode of tagalign. +func WithMode(mode Mode) Option { + return func(h *Helper) { + h.mode = mode + } +} + +// WithSort enable tags sort. +// fixedOrder specify the order of tags, the other tags will be sorted by name. +// Sory is disabled by default. +func WithSort(fixedOrder ...string) Option { + return func(h *Helper) { + h.sort = true + h.fixedTagOrder = fixedOrder + } +} + +// WithAlign configure whether enable tags align. +// Align is enabled by default. +func WithAlign(enabled bool) Option { + return func(h *Helper) { + h.align = enabled + } +} diff --git a/vendor/github.com/4meepo/tagalign/tagalign.go b/vendor/github.com/4meepo/tagalign/tagalign.go new file mode 100644 index 00000000..3dae96bc --- /dev/null +++ b/vendor/github.com/4meepo/tagalign/tagalign.go @@ -0,0 +1,350 @@ +package tagalign + +import ( + "fmt" + "go/ast" + "go/token" + "log" + "reflect" + "sort" + "strconv" + "strings" + + "github.com/fatih/structtag" + + "golang.org/x/tools/go/analysis" +) + +type Mode int + +const ( + StandaloneMode Mode = iota + GolangciLintMode +) + +func NewAnalyzer(options ...Option) *analysis.Analyzer { + return &analysis.Analyzer{ + Name: "tagalign", + Doc: "check that struct tags are well aligned", + Run: func(p *analysis.Pass) (any, error) { + Run(p, options...) + return nil, nil + }, + } +} + +func Run(pass *analysis.Pass, options ...Option) []Issue { + var issues []Issue + for _, f := range pass.Files { + h := &Helper{ + mode: StandaloneMode, + align: true, + } + for _, opt := range options { + opt(h) + } + + if !h.align && !h.sort { + // do nothing + return nil + } + + ast.Inspect(f, func(n ast.Node) bool { + h.find(pass, n) + return true + }) + h.Process(pass) + issues = append(issues, h.issues...) + } + return issues +} + +type Helper struct { + mode Mode + + align bool // whether enable tags align. + sort bool // whether enable tags sort. + fixedTagOrder []string // the order of tags, the other tags will be sorted by name. + + singleFields []*ast.Field + consecutiveFieldsGroups [][]*ast.Field // fields in this group, must be consecutive in struct. + issues []Issue +} + +// Issue is used to integrate with golangci-lint's inline auto fix. +type Issue struct { + Pos token.Position + Message string + InlineFix InlineFix +} +type InlineFix struct { + StartCol int // zero-based + Length int + NewString string +} + +func (w *Helper) find(pass *analysis.Pass, n ast.Node) { + v, ok := n.(*ast.StructType) + if !ok { + return + } + + fields := v.Fields.List + if len(fields) == 0 { + return + } + + fs := make([]*ast.Field, 0) + split := func() { + n := len(fs) + if n > 1 { + w.consecutiveFieldsGroups = append(w.consecutiveFieldsGroups, fs) + } else if n == 1 { + w.singleFields = append(w.singleFields, fs[0]) + } + + fs = nil + } + + for i, field := range fields { + if field.Tag == nil { + // field without tags + split() + continue + } + + if i > 0 { + if fields[i-1].Tag == nil { + // if previous filed do not have a tag + fs = append(fs, field) + continue + } + preLineNum := pass.Fset.Position(fields[i-1].Tag.Pos()).Line + lineNum := pass.Fset.Position(field.Tag.Pos()).Line + if lineNum-preLineNum > 1 { + // fields with tags are not consecutive, including two case: + // 1. splited by lines + // 2. splited by a struct + split() + + // check if the field is a struct + if _, ok := field.Type.(*ast.StructType); ok { + continue + } + } + } + + fs = append(fs, field) + } + + split() +} + +func (w *Helper) report(pass *analysis.Pass, field *ast.Field, startCol int, msg, replaceStr string) { + if w.mode == GolangciLintMode { + iss := Issue{ + Pos: pass.Fset.Position(field.Tag.Pos()), + Message: msg, + InlineFix: InlineFix{ + StartCol: startCol, + Length: len(field.Tag.Value), + NewString: replaceStr, + }, + } + w.issues = append(w.issues, iss) + } + + if w.mode == StandaloneMode { + pass.Report(analysis.Diagnostic{ + Pos: field.Tag.Pos(), + End: field.Tag.End(), + Message: msg, + SuggestedFixes: []analysis.SuggestedFix{ + { + Message: msg, + TextEdits: []analysis.TextEdit{ + { + Pos: field.Tag.Pos(), + End: field.Tag.End(), + NewText: []byte(replaceStr), + }, + }, + }, + }, + }) + } +} + +func (w *Helper) Process(pass *analysis.Pass) { //nolint:gocognit + // process grouped fields + for _, fields := range w.consecutiveFieldsGroups { + offsets := make([]int, len(fields)) + + var maxTagNum int + var tagsGroup, notSortedTagsGroup [][]*structtag.Tag + for i, field := range fields { + offsets[i] = pass.Fset.Position(field.Tag.Pos()).Column + tag, err := strconv.Unquote(field.Tag.Value) + if err != nil { + break + } + + tags, err := structtag.Parse(tag) + if err != nil { + break + } + + maxTagNum = max(maxTagNum, tags.Len()) + + if w.sort { + cp := make([]*structtag.Tag, tags.Len()) + for i, tag := range tags.Tags() { + cp[i] = tag + } + notSortedTagsGroup = append(notSortedTagsGroup, cp) + sortBy(w.fixedTagOrder, tags) + } + + tagsGroup = append(tagsGroup, tags.Tags()) + } + + // if w.align{ + // record the max length of each column tag + tagMaxLens := make([]int, maxTagNum) + + for j := 0; j < maxTagNum; j++ { + var maxLength int + for i := 0; i < len(tagsGroup); i++ { + if len(tagsGroup[i]) <= j { + // in case of index out of range + continue + } + maxLength = max(maxLength, len(tagsGroup[i][j].String())) + } + tagMaxLens[j] = maxLength + } + + for i, field := range fields { + tags := tagsGroup[i] + + var newTagStr string + if w.align { + // if align enabled, align tags. + newTagBuilder := strings.Builder{} + for i, tag := range tags { + format := alignFormat(tagMaxLens[i] + 1) // with an extra space + newTagBuilder.WriteString(fmt.Sprintf(format, tag.String())) + } + newTagStr = newTagBuilder.String() + } else { + // otherwise check if tags order changed + if w.sort && reflect.DeepEqual(notSortedTagsGroup[i], tags) { + // if tags order not changed, do nothing + continue + } + tagsStr := make([]string, len(tags)) + for i, tag := range tags { + tagsStr[i] = tag.String() + } + newTagStr = strings.Join(tagsStr, " ") + } + + unquoteTag := strings.TrimSpace(newTagStr) + newTagValue := fmt.Sprintf("`%s`", unquoteTag) + if field.Tag.Value == newTagValue { + // nothing changed + continue + } + + msg := "tag is not aligned, should be: " + unquoteTag + + w.report(pass, field, offsets[i]-1, msg, newTagValue) + } + } + + // process single fields + for _, field := range w.singleFields { + tag, err := strconv.Unquote(field.Tag.Value) + if err != nil { + continue + } + + tags, err := structtag.Parse(tag) + if err != nil { + continue + } + originalTags := append([]*structtag.Tag(nil), tags.Tags()...) + if w.sort { + sortBy(w.fixedTagOrder, tags) + } + + if reflect.DeepEqual(originalTags, tags.Tags()) { + // if tags order not changed, do nothing + continue + } + + newTagValue := fmt.Sprintf("`%s`", tags.String()) + if field.Tag.Value == newTagValue { + // nothing changed + continue + } + + msg := "tag is not aligned , should be: " + tags.String() + + w.report(pass, field, pass.Fset.Position(field.Tag.Pos()).Column-1, msg, newTagValue) + } +} + +// Issues returns all issues found by the analyzer. +// It is used to integrate with golangci-lint. +func (w *Helper) Issues() []Issue { + log.Println("tagalign 's Issues() should only be called in golangci-lint mode") + return w.issues +} + +// sortBy sorts tags by fixed order. +// If a tag is not in the fixed order, it will be sorted by name. +func sortBy(fixedOrder []string, tags *structtag.Tags) { + // sort by fixed order + sort.Slice(tags.Tags(), func(i, j int) bool { + ti := tags.Tags()[i] + tj := tags.Tags()[j] + + oi := findIndex(fixedOrder, ti.Key) + oj := findIndex(fixedOrder, tj.Key) + + if oi == -1 && oj == -1 { + return ti.Key < tj.Key + } + + if oi == -1 { + return false + } + + if oj == -1 { + return true + } + + return oi < oj + }) +} + +func findIndex(s []string, e string) int { + for i, a := range s { + if a == e { + return i + } + } + return -1 +} + +func alignFormat(length int) string { + return "%" + fmt.Sprintf("-%ds", length) +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} diff --git a/vendor/github.com/Antonboom/nilnil/pkg/analyzer/analyzer.go b/vendor/github.com/Antonboom/nilnil/pkg/analyzer/analyzer.go index 71a9ddf4..6bed7696 100644 --- a/vendor/github.com/Antonboom/nilnil/pkg/analyzer/analyzer.go +++ b/vendor/github.com/Antonboom/nilnil/pkg/analyzer/analyzer.go @@ -50,15 +50,17 @@ var ( } ) -type typeSpecByName map[string]*ast.TypeSpec +type typeSpecByName map[string]typer func (n *nilNil) run(pass *analysis.Pass) (interface{}, error) { insp := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) - typeSpecs := typeSpecByName{} + typeSpecs := typeSpecByName{ + "any": newTyper(new(ast.InterfaceType)), + } insp.Preorder(types, func(node ast.Node) { t := node.(*ast.TypeSpec) - typeSpecs[t.Name.Name] = t + typeSpecs[t.Name.Name] = newTyper(t.Type) }) var fs funcTypeStack @@ -125,7 +127,7 @@ func (n *nilNil) isDangerNilType(t ast.Expr, typeSpecs typeSpecByName) bool { case *ast.Ident: if t, ok := typeSpecs[v.Name]; ok { - return n.isDangerNilType(t.Type, nil) + return n.isDangerNilType(t.Type(), typeSpecs) } } return false @@ -146,3 +148,11 @@ func isIdent(n ast.Node, name string) bool { } return i.Name == name } + +type typer interface { + Type() ast.Expr +} + +func newTyper(t ast.Expr) typer { return typerImpl{t: t} } // +type typerImpl struct{ t ast.Expr } // +func (ti typerImpl) Type() ast.Expr { return ti.t } diff --git a/vendor/github.com/BurntSushi/toml/decode.go b/vendor/github.com/BurntSushi/toml/decode.go index 0ca1dc4f..dac2dc28 100644 --- a/vendor/github.com/BurntSushi/toml/decode.go +++ b/vendor/github.com/BurntSushi/toml/decode.go @@ -91,7 +91,7 @@ const ( // UnmarshalText method. See the Unmarshaler example for a demonstration with // email addresses. // -// ### Key mapping +// # Key mapping // // TOML keys can map to either keys in a Go map or field names in a Go struct. // The special `toml` struct tag can be used to map TOML keys to struct fields diff --git a/vendor/github.com/BurntSushi/toml/deprecated.go b/vendor/github.com/BurntSushi/toml/deprecated.go index c6af3f23..b9e30971 100644 --- a/vendor/github.com/BurntSushi/toml/deprecated.go +++ b/vendor/github.com/BurntSushi/toml/deprecated.go @@ -5,17 +5,25 @@ import ( "io" ) +// TextMarshaler is an alias for encoding.TextMarshaler. +// // Deprecated: use encoding.TextMarshaler type TextMarshaler encoding.TextMarshaler +// TextUnmarshaler is an alias for encoding.TextUnmarshaler. +// // Deprecated: use encoding.TextUnmarshaler type TextUnmarshaler encoding.TextUnmarshaler +// PrimitiveDecode is an alias for MetaData.PrimitiveDecode(). +// // Deprecated: use MetaData.PrimitiveDecode. func PrimitiveDecode(primValue Primitive, v interface{}) error { md := MetaData{decoded: make(map[string]struct{})} return md.unify(primValue.undecoded, rvalue(v)) } +// DecodeReader is an alias for NewDecoder(r).Decode(v). +// // Deprecated: use NewDecoder(reader).Decode(&value). func DecodeReader(r io.Reader, v interface{}) (MetaData, error) { return NewDecoder(r).Decode(v) } diff --git a/vendor/github.com/BurntSushi/toml/encode.go b/vendor/github.com/BurntSushi/toml/encode.go index 930e1d52..15a0ec39 100644 --- a/vendor/github.com/BurntSushi/toml/encode.go +++ b/vendor/github.com/BurntSushi/toml/encode.go @@ -136,6 +136,9 @@ func NewEncoder(w io.Writer) *Encoder { // document. func (enc *Encoder) Encode(v interface{}) error { rv := eindirect(reflect.ValueOf(v)) + + // XXX + if err := enc.safeEncode(Key([]string{}), rv); err != nil { return err } @@ -457,6 +460,16 @@ func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) { frv := eindirect(rv.Field(i)) + if is32Bit { + // Copy so it works correct on 32bit archs; not clear why this + // is needed. See #314, and https://www.reddit.com/r/golang/comments/pnx8v4 + // This also works fine on 64bit, but 32bit archs are somewhat + // rare and this is a wee bit faster. + copyStart := make([]int, len(start)) + copy(copyStart, start) + start = copyStart + } + // Treat anonymous struct fields with tag names as though they are // not anonymous, like encoding/json does. // @@ -471,17 +484,7 @@ func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) { if typeIsTable(tomlTypeOfGo(frv)) { fieldsSub = append(fieldsSub, append(start, f.Index...)) } else { - // Copy so it works correct on 32bit archs; not clear why this - // is needed. See #314, and https://www.reddit.com/r/golang/comments/pnx8v4 - // This also works fine on 64bit, but 32bit archs are somewhat - // rare and this is a wee bit faster. - if is32Bit { - copyStart := make([]int, len(start)) - copy(copyStart, start) - fieldsDirect = append(fieldsDirect, append(copyStart, f.Index...)) - } else { - fieldsDirect = append(fieldsDirect, append(start, f.Index...)) - } + fieldsDirect = append(fieldsDirect, append(start, f.Index...)) } } } @@ -490,24 +493,27 @@ func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) { writeFields := func(fields [][]int) { for _, fieldIndex := range fields { fieldType := rt.FieldByIndex(fieldIndex) - fieldVal := eindirect(rv.FieldByIndex(fieldIndex)) + fieldVal := rv.FieldByIndex(fieldIndex) - if isNil(fieldVal) { /// Don't write anything for nil fields. + opts := getOptions(fieldType.Tag) + if opts.skip { + continue + } + if opts.omitempty && isEmpty(fieldVal) { continue } - opts := getOptions(fieldType.Tag) - if opts.skip { + fieldVal = eindirect(fieldVal) + + if isNil(fieldVal) { // Don't write anything for nil fields. continue } + keyName := fieldType.Name if opts.name != "" { keyName = opts.name } - if opts.omitempty && enc.isEmpty(fieldVal) { - continue - } if opts.omitzero && isZero(fieldVal) { continue } @@ -649,7 +655,7 @@ func isZero(rv reflect.Value) bool { return false } -func (enc *Encoder) isEmpty(rv reflect.Value) bool { +func isEmpty(rv reflect.Value) bool { switch rv.Kind() { case reflect.Array, reflect.Slice, reflect.Map, reflect.String: return rv.Len() == 0 @@ -664,13 +670,15 @@ func (enc *Encoder) isEmpty(rv reflect.Value) bool { // type b struct{ s []string } // s := a{field: b{s: []string{"AAA"}}} for i := 0; i < rv.NumField(); i++ { - if !enc.isEmpty(rv.Field(i)) { + if !isEmpty(rv.Field(i)) { return false } } return true case reflect.Bool: return !rv.Bool() + case reflect.Ptr: + return rv.IsNil() } return false } @@ -693,8 +701,11 @@ func (enc *Encoder) newline() { // v v v v vv // key = {k = 1, k2 = 2} func (enc *Encoder) writeKeyValue(key Key, val reflect.Value, inline bool) { + /// Marshaler used on top-level document; call eElement() to just call + /// Marshal{TOML,Text}. if len(key) == 0 { - encPanic(errNoKey) + enc.eElement(val) + return } enc.wf("%s%s = ", enc.indentStr(key), key.maybeQuoted(len(key)-1)) enc.eElement(val) diff --git a/vendor/github.com/BurntSushi/toml/error.go b/vendor/github.com/BurntSushi/toml/error.go index f4f390e6..efd68865 100644 --- a/vendor/github.com/BurntSushi/toml/error.go +++ b/vendor/github.com/BurntSushi/toml/error.go @@ -84,7 +84,7 @@ func (pe ParseError) Error() string { pe.Position.Line, pe.LastKey, msg) } -// ErrorWithUsage() returns the error with detailed location context. +// ErrorWithPosition returns the error with detailed location context. // // See the documentation on [ParseError]. func (pe ParseError) ErrorWithPosition() string { @@ -124,7 +124,7 @@ func (pe ParseError) ErrorWithPosition() string { return b.String() } -// ErrorWithUsage() returns the error with detailed location context and usage +// ErrorWithUsage returns the error with detailed location context and usage // guidance. // // See the documentation on [ParseError]. diff --git a/vendor/github.com/BurntSushi/toml/lex.go b/vendor/github.com/BurntSushi/toml/lex.go index d4d70871..a2545302 100644 --- a/vendor/github.com/BurntSushi/toml/lex.go +++ b/vendor/github.com/BurntSushi/toml/lex.go @@ -618,6 +618,9 @@ func lexInlineTableValue(lx *lexer) stateFn { case isWhitespace(r): return lexSkip(lx, lexInlineTableValue) case isNL(r): + if tomlNext { + return lexSkip(lx, lexInlineTableValue) + } return lx.errorPrevLine(errLexInlineTableNL{}) case r == '#': lx.push(lexInlineTableValue) @@ -640,6 +643,9 @@ func lexInlineTableValueEnd(lx *lexer) stateFn { case isWhitespace(r): return lexSkip(lx, lexInlineTableValueEnd) case isNL(r): + if tomlNext { + return lexSkip(lx, lexInlineTableValueEnd) + } return lx.errorPrevLine(errLexInlineTableNL{}) case r == '#': lx.push(lexInlineTableValueEnd) @@ -648,6 +654,9 @@ func lexInlineTableValueEnd(lx *lexer) stateFn { lx.ignore() lx.skip(isWhitespace) if lx.peek() == '}' { + if tomlNext { + return lexInlineTableValueEnd + } return lx.errorf("trailing comma not allowed in inline tables") } return lexInlineTableValue @@ -770,8 +779,8 @@ func lexRawString(lx *lexer) stateFn { } } -// lexMultilineRawString consumes a raw string. Nothing can be escaped in such -// a string. It assumes that the beginning ''' has already been consumed and +// lexMultilineRawString consumes a raw string. Nothing can be escaped in such a +// string. It assumes that the beginning triple-' has already been consumed and // ignored. func lexMultilineRawString(lx *lexer) stateFn { r := lx.next() @@ -828,6 +837,11 @@ func lexMultilineStringEscape(lx *lexer) stateFn { func lexStringEscape(lx *lexer) stateFn { r := lx.next() switch r { + case 'e': + if !tomlNext { + return lx.error(errLexEscape{r}) + } + fallthrough case 'b': fallthrough case 't': @@ -846,6 +860,11 @@ func lexStringEscape(lx *lexer) stateFn { fallthrough case '\\': return lx.pop() + case 'x': + if !tomlNext { + return lx.error(errLexEscape{r}) + } + return lexHexEscape case 'u': return lexShortUnicodeEscape case 'U': @@ -854,6 +873,19 @@ func lexStringEscape(lx *lexer) stateFn { return lx.error(errLexEscape{r}) } +func lexHexEscape(lx *lexer) stateFn { + var r rune + for i := 0; i < 2; i++ { + r = lx.next() + if !isHexadecimal(r) { + return lx.errorf( + `expected two hexadecimal digits after '\x', but got %q instead`, + lx.current()) + } + } + return lx.pop() +} + func lexShortUnicodeEscape(lx *lexer) stateFn { var r rune for i := 0; i < 4; i++ { @@ -1225,7 +1257,23 @@ func isOctal(r rune) bool { return r >= '0' && r <= '7' } func isHexadecimal(r rune) bool { return (r >= '0' && r <= '9') || (r >= 'a' && r <= 'f') || (r >= 'A' && r <= 'F') } + func isBareKeyChar(r rune) bool { + if tomlNext { + return (r >= 'A' && r <= 'Z') || + (r >= 'a' && r <= 'z') || + (r >= '0' && r <= '9') || + r == '_' || r == '-' || + r == 0xb2 || r == 0xb3 || r == 0xb9 || (r >= 0xbc && r <= 0xbe) || + (r >= 0xc0 && r <= 0xd6) || (r >= 0xd8 && r <= 0xf6) || (r >= 0xf8 && r <= 0x037d) || + (r >= 0x037f && r <= 0x1fff) || + (r >= 0x200c && r <= 0x200d) || (r >= 0x203f && r <= 0x2040) || + (r >= 0x2070 && r <= 0x218f) || (r >= 0x2460 && r <= 0x24ff) || + (r >= 0x2c00 && r <= 0x2fef) || (r >= 0x3001 && r <= 0xd7ff) || + (r >= 0xf900 && r <= 0xfdcf) || (r >= 0xfdf0 && r <= 0xfffd) || + (r >= 0x10000 && r <= 0xeffff) + } + return (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9') || diff --git a/vendor/github.com/BurntSushi/toml/parse.go b/vendor/github.com/BurntSushi/toml/parse.go index d2542d6f..a4e74bfe 100644 --- a/vendor/github.com/BurntSushi/toml/parse.go +++ b/vendor/github.com/BurntSushi/toml/parse.go @@ -2,6 +2,7 @@ package toml import ( "fmt" + "os" "strconv" "strings" "time" @@ -10,6 +11,11 @@ import ( "github.com/BurntSushi/toml/internal" ) +var tomlNext = func() bool { + _, ok := os.LookupEnv("BURNTSUSHI_TOML_110") + return ok +}() + type parser struct { lx *lexer context Key // Full key for the current hash in scope. @@ -41,9 +47,12 @@ func parse(data string) (p *parser, err error) { }() // Read over BOM; do this here as the lexer calls utf8.DecodeRuneInString() - // which mangles stuff. - if strings.HasPrefix(data, "\xff\xfe") || strings.HasPrefix(data, "\xfe\xff") { + // which mangles stuff. UTF-16 BOM isn't strictly valid, but some tools add + // it anyway. + if strings.HasPrefix(data, "\xff\xfe") || strings.HasPrefix(data, "\xfe\xff") { // UTF-16 data = data[2:] + } else if strings.HasPrefix(data, "\xef\xbb\xbf") { // UTF-8 + data = data[3:] } // Examine first few bytes for NULL bytes; this probably means it's a UTF-16 @@ -236,7 +245,7 @@ func (p *parser) value(it item, parentIsArray bool) (interface{}, tomlType) { case itemString: return p.replaceEscapes(it, it.val), p.typeOfPrimitive(it) case itemMultilineString: - return p.replaceEscapes(it, stripFirstNewline(p.stripEscapedNewlines(it.val))), p.typeOfPrimitive(it) + return p.replaceEscapes(it, p.stripEscapedNewlines(stripFirstNewline(it.val))), p.typeOfPrimitive(it) case itemRawString: return it.val, p.typeOfPrimitive(it) case itemRawMultilineString: @@ -331,11 +340,17 @@ func (p *parser) valueFloat(it item) (interface{}, tomlType) { var dtTypes = []struct { fmt string zone *time.Location + next bool }{ - {time.RFC3339Nano, time.Local}, - {"2006-01-02T15:04:05.999999999", internal.LocalDatetime}, - {"2006-01-02", internal.LocalDate}, - {"15:04:05.999999999", internal.LocalTime}, + {time.RFC3339Nano, time.Local, false}, + {"2006-01-02T15:04:05.999999999", internal.LocalDatetime, false}, + {"2006-01-02", internal.LocalDate, false}, + {"15:04:05.999999999", internal.LocalTime, false}, + + // tomlNext + {"2006-01-02T15:04Z07:00", time.Local, true}, + {"2006-01-02T15:04", internal.LocalDatetime, true}, + {"15:04", internal.LocalTime, true}, } func (p *parser) valueDatetime(it item) (interface{}, tomlType) { @@ -346,6 +361,9 @@ func (p *parser) valueDatetime(it item) (interface{}, tomlType) { err error ) for _, dt := range dtTypes { + if dt.next && !tomlNext { + continue + } t, err = time.ParseInLocation(dt.fmt, it.val, dt.zone) if err == nil { ok = true @@ -384,6 +402,7 @@ func (p *parser) valueArray(it item) (interface{}, tomlType) { // // Not entirely sure how to best store this; could use "key[0]", // "key[1]" notation, or maybe store it on the Array type? + _ = types } return array, tomlArray } @@ -662,49 +681,54 @@ func stripFirstNewline(s string) string { return s } -// Remove newlines inside triple-quoted strings if a line ends with "\". +// stripEscapedNewlines removes whitespace after line-ending backslashes in +// multiline strings. +// +// A line-ending backslash is an unescaped \ followed only by whitespace until +// the next newline. After a line-ending backslash, all whitespace is removed +// until the next non-whitespace character. func (p *parser) stripEscapedNewlines(s string) string { - split := strings.Split(s, "\n") - if len(split) < 1 { - return s - } - - escNL := false // Keep track of the last non-blank line was escaped. - for i, line := range split { - line = strings.TrimRight(line, " \t\r") - - if len(line) == 0 || line[len(line)-1] != '\\' { - split[i] = strings.TrimRight(split[i], "\r") - if !escNL && i != len(split)-1 { - split[i] += "\n" - } - continue + var b strings.Builder + var i int + for { + ix := strings.Index(s[i:], `\`) + if ix < 0 { + b.WriteString(s) + return b.String() } + i += ix - escBS := true - for j := len(line) - 1; j >= 0 && line[j] == '\\'; j-- { - escBS = !escBS + if len(s) > i+1 && s[i+1] == '\\' { + // Escaped backslash. + i += 2 + continue } - if escNL { - line = strings.TrimLeft(line, " \t\r") + // Scan until the next non-whitespace. + j := i + 1 + whitespaceLoop: + for ; j < len(s); j++ { + switch s[j] { + case ' ', '\t', '\r', '\n': + default: + break whitespaceLoop + } } - escNL = !escBS - - if escBS { - split[i] += "\n" + if j == i+1 { + // Not a whitespace escape. + i++ continue } - - if i == len(split)-1 { - p.panicf("invalid escape: '\\ '") - } - - split[i] = line[:len(line)-1] // Remove \ - if len(split)-1 > i { - split[i+1] = strings.TrimLeft(split[i+1], " \t\r") + if !strings.Contains(s[i:j], "\n") { + // This is not a line-ending backslash. + // (It's a bad escape sequence, but we can let + // replaceEscapes catch it.) + i++ + continue } + b.WriteString(s[:i]) + s = s[j:] + i = 0 } - return strings.Join(split, "") } func (p *parser) replaceEscapes(it item, str string) string { @@ -743,12 +767,23 @@ func (p *parser) replaceEscapes(it item, str string) string { case 'r': replaced = append(replaced, rune(0x000D)) r += 1 + case 'e': + if tomlNext { + replaced = append(replaced, rune(0x001B)) + r += 1 + } case '"': replaced = append(replaced, rune(0x0022)) r += 1 case '\\': replaced = append(replaced, rune(0x005C)) r += 1 + case 'x': + if tomlNext { + escaped := p.asciiEscapeToUnicode(it, s[r+1:r+3]) + replaced = append(replaced, escaped) + r += 3 + } case 'u': // At this point, we know we have a Unicode escape of the form // `uXXXX` at [r, r+5). (Because the lexer guarantees this diff --git a/vendor/github.com/OpenPeeDeeP/depguard/README.md b/vendor/github.com/OpenPeeDeeP/depguard/README.md deleted file mode 100644 index 07e9f915..00000000 --- a/vendor/github.com/OpenPeeDeeP/depguard/README.md +++ /dev/null @@ -1,111 +0,0 @@ -# Depguard - -Go linter that checks package imports are in a list of acceptable packages. It -can also deny a list of packages and can do prefix or glob matching. -This allows you to allow imports from a whole organization or only -allow specific packages within a repository. It is recommended to use prefix -matching as it is faster than glob matching. The fewer glob matches the better. - -> If a pattern is matched by prefix it does not try to match via glob. - -## Install - -```bash -go get -u github.com/OpenPeeDeeP/depguard -``` - -## Config - -By default, Depguard looks for a file named `.depguard.json` in the current -current working directory. If it is somewhere else, pass in the `-c` flag with -the location of your configuration file. - -The following is an example configuration file. - -```json -{ - "type": "allowlist", - "packages": ["github.com/OpenPeeDeeP/depguard"], - "packageErrorMessages": { - "github.com/OpenPeeDeeP/depguards": "Please use \"github.com/OpenPeeDeeP/depguard\"," - }, - "inTests": ["github.com/stretchr/testify"], - "includeGoStdLib": true -} -``` - -- `type` can be either `allowlist` or `denylist`. This check is case insensitive. - If not specified the default is `denylist`. The values `whitelist` and `blacklist` - are also accepted for backwards compatibility. -- `packages` is a list of packages for the list type specified. -- `packageErrorMessages` is a mapping from packages to the error message to display -- `inTests` is a list of packages allowed/disallowed only in test files. -- Set `includeGoStdLib` (`includeGoRoot` for backwards compatibility) to true if you want to check the list against standard lib. - If not specified the default is false. - -### Ignore File Rules - -The configuration also allows us to specify rules to ignore certain files considered by the linter. This means that we need not apply package import checks across our entire code base. - -For example, consider the following configuration to block a test package: -```json -{ - "type": "denylist", - "packages": ["github.com/stretchr/testify"], - "inTests": ["github.com/stretchr/testify"] -} -``` - -We can use a `ignoreFileRules` field to write a configuration that only considers test files: -```json -{ - "type": "denylist", - "packages": ["github.com/stretchr/testify"], - "ignoreFileRules": ["!**/*_test.go"] -} -``` - -Or if we wanted to consider only non-test files: -```json -{ - "type": "denylist", - "packages": ["github.com/stretchr/testify"], - "ignoreFileRules": ["**/*_test.go"] -} -``` - -Like the `packages` field, the `ignoreFileRules` field can accept both string prefixes and string glob patterns. Note in the first example above, the use of the `!` character in front of the rule. This is a special character which signals that the linter should negate the rule. This allows for more precise control, but it is only available for glob patterns. - -## Gometalinter - -The binary installation of this linter can be used with -[Gometalinter](https://github.com/alecthomas/gometalinter). - -If you use a configuration file for Gometalinter then the following will need to -be added to your configuration file. - -```json -{ - "linters": { - "depguard": { - "command": "depguard -c path/to/config.json", - "pattern": "PATH:LINE:COL:MESSAGE", - "installFrom": "github.com/OpenPeeDeeP/depguard", - "isFast": true, - "partitionStrategy": "packages" - } - } -} -``` - -If you prefer the command line way the following will work for you as well. - -```bash -gometalinter --linter='depguard:depguard -c path/to/config.json:PATH:LINE:COL:MESSAGE' -``` - -## Golangci-lint - -This linter was built with -[Golangci-lint](https://github.com/golangci/golangci-lint) in mind. It is compatible -and read their docs to see how to implement all their linters, including this one. diff --git a/vendor/github.com/OpenPeeDeeP/depguard/depguard.go b/vendor/github.com/OpenPeeDeeP/depguard/depguard.go deleted file mode 100644 index d7011cd9..00000000 --- a/vendor/github.com/OpenPeeDeeP/depguard/depguard.go +++ /dev/null @@ -1,313 +0,0 @@ -package depguard - -import ( - "go/build" - "go/token" - "io/ioutil" - "path" - "sort" - "strings" - "sync" - - "github.com/gobwas/glob" - "golang.org/x/tools/go/loader" -) - -// ListType states what kind of list is passed in. -type ListType int - -const ( - // LTBlacklist states the list given is a blacklist. (default) - LTBlacklist ListType = iota - // LTWhitelist states the list given is a whitelist. - LTWhitelist -) - -// StringToListType makes it easier to turn a string into a ListType. -// It assumes that the string representation is lower case. -var StringToListType = map[string]ListType{ - "allowlist": LTWhitelist, - "denylist": LTBlacklist, - "whitelist": LTWhitelist, - "blacklist": LTBlacklist, -} - -// Issue with the package with PackageName at the Position. -type Issue struct { - PackageName string - Position token.Position -} - -// Wrapper for glob patterns that allows for custom negation -type negatableGlob struct { - g glob.Glob - negate bool -} - -// Depguard checks imports to make sure they follow the given list and constraints. -type Depguard struct { - ListType ListType - IncludeGoRoot bool - - Packages []string - prefixPackages []string - globPackages []glob.Glob - - TestPackages []string - prefixTestPackages []string - globTestPackages []glob.Glob - - IgnoreFileRules []string - prefixIgnoreFileRules []string - globIgnoreFileRules []negatableGlob - - prefixRoot []string - - isInitialized bool - isInitializedMutex sync.Mutex -} - -// Run checks for dependencies given the program and validates them against -// Packages. -func (dg *Depguard) Run(config *loader.Config, prog *loader.Program) ([]*Issue, error) { - // Shortcut execution on an empty blacklist as that means every package is allowed - if dg.ListType == LTBlacklist && len(dg.Packages) == 0 { - return nil, nil - } - - if err := dg.initialize(config, prog); err != nil { - return nil, err - } - directImports, err := dg.createImportMap(prog) - if err != nil { - return nil, err - } - var issues []*Issue - for pkg, positions := range directImports { - for _, pos := range positions { - if ignoreFile(pos.Filename, dg.prefixIgnoreFileRules, dg.globIgnoreFileRules) { - continue - } - - prefixList, globList := dg.prefixPackages, dg.globPackages - if len(dg.TestPackages) > 0 && strings.Index(pos.Filename, "_test.go") != -1 { - prefixList, globList = dg.prefixTestPackages, dg.globTestPackages - } - - if dg.flagIt(pkg, prefixList, globList) { - issues = append(issues, &Issue{ - PackageName: pkg, - Position: pos, - }) - } - } - } - return issues, nil -} - -func (dg *Depguard) initialize(config *loader.Config, prog *loader.Program) error { - dg.isInitializedMutex.Lock() - defer dg.isInitializedMutex.Unlock() - - if dg.isInitialized { - return nil - } - - // parse ordinary guarded packages - for _, pkg := range dg.Packages { - if strings.ContainsAny(pkg, "!?*[]{}") { - g, err := glob.Compile(pkg, '/') - if err != nil { - return err - } - dg.globPackages = append(dg.globPackages, g) - } else { - dg.prefixPackages = append(dg.prefixPackages, pkg) - } - } - - // Sort the packages so we can have a faster search in the array - sort.Strings(dg.prefixPackages) - - // parse guarded tests packages - for _, pkg := range dg.TestPackages { - if strings.ContainsAny(pkg, "!?*[]{}") { - g, err := glob.Compile(pkg, '/') - if err != nil { - return err - } - dg.globTestPackages = append(dg.globTestPackages, g) - } else { - dg.prefixTestPackages = append(dg.prefixTestPackages, pkg) - } - } - - // Sort the test packages so we can have a faster search in the array - sort.Strings(dg.prefixTestPackages) - - // parse ignore file rules - for _, rule := range dg.IgnoreFileRules { - if strings.ContainsAny(rule, "!?*[]{}") { - ng := negatableGlob{} - if strings.HasPrefix(rule, "!") { - ng.negate = true - rule = rule[1:] // Strip out the leading '!' - } else { - ng.negate = false - } - - g, err := glob.Compile(rule, '/') - if err != nil { - return err - } - ng.g = g - - dg.globIgnoreFileRules = append(dg.globIgnoreFileRules, ng) - } else { - dg.prefixIgnoreFileRules = append(dg.prefixIgnoreFileRules, rule) - } - } - - // Sort the rules so we can have a faster search in the array - sort.Strings(dg.prefixIgnoreFileRules) - - if !dg.IncludeGoRoot { - var err error - dg.prefixRoot, err = listRootPrefixs(config.Build) - if err != nil { - return err - } - } - - dg.isInitialized = true - return nil -} - -func (dg *Depguard) createImportMap(prog *loader.Program) (map[string][]token.Position, error) { - importMap := make(map[string][]token.Position) - // For the directly imported packages - for _, imported := range prog.InitialPackages() { - // Go through their files - for _, file := range imported.Files { - // And populate a map of all direct imports and their positions - // This will filter out GoRoot depending on the Depguard.IncludeGoRoot - for _, fileImport := range file.Imports { - fileImportPath := cleanBasicLitString(fileImport.Path.Value) - if !dg.IncludeGoRoot && dg.isRoot(fileImportPath) { - continue - } - position := prog.Fset.Position(fileImport.Pos()) - positions, found := importMap[fileImportPath] - if !found { - importMap[fileImportPath] = []token.Position{ - position, - } - continue - } - importMap[fileImportPath] = append(positions, position) - } - } - } - return importMap, nil -} - -func ignoreFile(filename string, prefixList []string, negatableGlobList []negatableGlob) bool { - if strInPrefixList(filename, prefixList) { - return true - } - return strInNegatableGlobList(filename, negatableGlobList) -} - -func pkgInList(pkg string, prefixList []string, globList []glob.Glob) bool { - if strInPrefixList(pkg, prefixList) { - return true - } - return strInGlobList(pkg, globList) -} - -func strInPrefixList(str string, prefixList []string) bool { - // Idx represents where in the prefix slice the passed in string would go - // when sorted. -1 Just means that it would be at the very front of the slice. - idx := sort.Search(len(prefixList), func(i int) bool { - return prefixList[i] > str - }) - 1 - // This means that the string passed in has no way to be prefixed by anything - // in the prefix list as it is already smaller then everything - if idx == -1 { - return false - } - return strings.HasPrefix(str, prefixList[idx]) -} - -func strInGlobList(str string, globList []glob.Glob) bool { - for _, g := range globList { - if g.Match(str) { - return true - } - } - return false -} - -func strInNegatableGlobList(str string, negatableGlobList []negatableGlob) bool { - for _, ng := range negatableGlobList { - // Return true when: - // - Match is true and negate is off - // - Match is false and negate is on - if ng.g.Match(str) != ng.negate { - return true - } - } - return false -} - -// InList | WhiteList | BlackList -// y | | x -// n | x | -func (dg *Depguard) flagIt(pkg string, prefixList []string, globList []glob.Glob) bool { - return pkgInList(pkg, prefixList, globList) == (dg.ListType == LTBlacklist) -} - -func cleanBasicLitString(value string) string { - return strings.Trim(value, "\"\\") -} - -// We can do this as all imports that are not root are either prefixed with a domain -// or prefixed with `./` or `/` to dictate it is a local file reference -func listRootPrefixs(buildCtx *build.Context) ([]string, error) { - if buildCtx == nil { - buildCtx = &build.Default - } - root := path.Join(buildCtx.GOROOT, "src") - fs, err := ioutil.ReadDir(root) - if err != nil { - return nil, err - } - var pkgPrefix []string - for _, f := range fs { - if !f.IsDir() { - continue - } - pkgPrefix = append(pkgPrefix, f.Name()) - } - return pkgPrefix, nil -} - -func (dg *Depguard) isRoot(importPath string) bool { - // Idx represents where in the package slice the passed in package would go - // when sorted. -1 Just means that it would be at the very front of the slice. - idx := sort.Search(len(dg.prefixRoot), func(i int) bool { - return dg.prefixRoot[i] > importPath - }) - 1 - // This means that the package passed in has no way to be prefixed by anything - // in the package list as it is already smaller then everything - if idx == -1 { - return false - } - // if it is prefixed by a root prefix we need to check if it is an exact match - // or prefix with `/` as this could return false posative if the domain was - // `archive.com` for example as `archive` is a go root package. - if strings.HasPrefix(importPath, dg.prefixRoot[idx]) { - return strings.HasPrefix(importPath, dg.prefixRoot[idx]+"/") || importPath == dg.prefixRoot[idx] - } - return false -} diff --git a/vendor/github.com/OpenPeeDeeP/depguard/.gitignore b/vendor/github.com/OpenPeeDeeP/depguard/v2/.gitignore similarity index 100% rename from vendor/github.com/OpenPeeDeeP/depguard/.gitignore rename to vendor/github.com/OpenPeeDeeP/depguard/v2/.gitignore diff --git a/vendor/github.com/OpenPeeDeeP/depguard/LICENSE b/vendor/github.com/OpenPeeDeeP/depguard/v2/LICENSE similarity index 100% rename from vendor/github.com/OpenPeeDeeP/depguard/LICENSE rename to vendor/github.com/OpenPeeDeeP/depguard/v2/LICENSE diff --git a/vendor/github.com/OpenPeeDeeP/depguard/v2/README.md b/vendor/github.com/OpenPeeDeeP/depguard/v2/README.md new file mode 100644 index 00000000..3de3f631 --- /dev/null +++ b/vendor/github.com/OpenPeeDeeP/depguard/v2/README.md @@ -0,0 +1,146 @@ +# Depguard + +A Go linter that checks package imports are in a list of acceptable packages. +This allows you to allow imports from a whole organization or only +allow specific packages within a repository. + +## Install + +```bash +go get github.com/OpenPeeDeeP/depguard/v2 +``` + +## Config + +The Depguard binary looks for a file named `^\.?depguard\.(yaml|yml|json|toml)$` in the current +current working directory. Examples include (`.depguard.yml` or `depguard.toml`). + +The following is an example configuration file. + +```json +{ + "main": { + "files": [ + "$all", + "!$test" + ], + "allow": [ + "$gostd", + "github.com/OpenPeeDeeP" + ], + "deny": { + "reflect": "Who needs reflection", + } + }, + "tests": { + "files": [ + "$test" + ], + "deny": { + "github.com/stretchr/testify": "Please use standard library for tests" + } + } +} +``` + +- The top level is a map of lists. The key of the map is a name that shows up in +the linter's output. +- `files` - list of file globs that will match this list of settings to compare against +- `allow` - list of allowed packages +- `deny` - map of packages that are not allowed where the value is a suggestion + +Files are matched using [Globs](https://github.com/gobwas/glob). If the files +list is empty, then all files will match that list. Prefixing a file +with an exclamation mark `!` will put that glob in a "don't match" list. A file +will match a list if it is allowed and not denied. + +> Should always prefix a file glob with `**/` as files are matched against absolute paths. + +Allow is a prefix of packages to allow. A dollar sign `$` can be used at the end +of a package to specify it must be exact match only. + +Deny is a map where the key is a prefix of the package to deny, and the value +is a suggestion on what to use instead. A dollar sign `$` can be used at the end +of a package to specify it must be exact match only. + +A Prefix List just means that a package will match a value, if the value is a +prefix of the package. Example `github.com/OpenPeeDeeP/depguard` package will match +a value of `github.com/OpenPeeDeeP` but won't match `github.com/OpenPeeDeeP/depguard/v2`. + +### Variables + +There are variable replacements for each type of list (file or package). This is +to reduce repetition and tedious behaviors. + +#### File Variables + +> you can still use and exclamation mark `!` in front of a variable to say not to +use it. Example `!$test` will match any file that is not a go test file. + +- `$all` - matches all go files +- `$test` - matches all go test files + +#### Package Variables + +- `$gostd` - matches all of go's standard library (Pulled from GOROOT) + +### Example Configs + +Below: + +- non-test go files will match `Main` and test go files will match `Test`. +- both allow all of go standard library except for the `reflect` package which will +tell the user "Please don't use reflect package". +- go test files are also allowed to use https://github.com/stretchr/testify package +and any sub-package of it. + +```yaml +Main: + files: + - $all + - "!$test" + allow: + - $gostd + deny: + reflect: Please don't use reflect package +Test: + files: + - $test + allow: + - $gostd + - github.com/stretchr/testify + deny: + reflect: Please don't use reflect package +``` + +Below: + +- All go files will match `Main` +- Go files in internal will match both `Main` and `Internal` + +```yaml +Main: + files: + - $all +Internal: + files: + - "**/internal/**/*.go" +``` + +Below: + +- All packages are allowed except for `github.com/OpenPeeDeeP/depguard`. Though +`github.com/OpenPeeDeeP/depguard/v2` and `github.com/OpenPeeDeeP/depguard/somepackage` +would be allowed. + +```yaml +Main: + deny: + - github.com/OpenPeeDeeP/depguard$ +``` + +## Golangci-lint + +This linter was built with +[Golangci-lint](https://github.com/golangci/golangci-lint) in mind. It is compatible +and read their docs to see how to implement all their linters, including this one. diff --git a/vendor/github.com/OpenPeeDeeP/depguard/v2/depguard.go b/vendor/github.com/OpenPeeDeeP/depguard/v2/depguard.go new file mode 100644 index 00000000..13170805 --- /dev/null +++ b/vendor/github.com/OpenPeeDeeP/depguard/v2/depguard.go @@ -0,0 +1,58 @@ +package depguard + +import ( + "fmt" + "go/ast" + "path/filepath" + "strings" + + "golang.org/x/tools/go/analysis" +) + +// NewAnalyzer creates a new analyzer from the settings passed in +func NewAnalyzer(settings *LinterSettings) (*analysis.Analyzer, error) { + s, err := settings.compile() + if err != nil { + return nil, err + } + analyzer := newAnalyzer(s) + return analyzer, nil +} + +func newAnalyzer(settings linterSettings) *analysis.Analyzer { + return &analysis.Analyzer{ + Name: "depguard", + Doc: "Go linter that checks if package imports are in a list of acceptable packages", + Run: settings.run, + RunDespiteErrors: false, + } +} + +func (s linterSettings) run(pass *analysis.Pass) (interface{}, error) { + for _, file := range pass.Files { + // For Windows need to replace separator with '/' + fileName := filepath.ToSlash(pass.Fset.Position(file.Pos()).Filename) + lists := s.whichLists(fileName) + for _, imp := range file.Imports { + for _, l := range lists { + if allowed, sugg := l.importAllowed(rawBasicLit(imp.Path)); !allowed { + diag := analysis.Diagnostic{ + Pos: imp.Pos(), + End: imp.End(), + Message: fmt.Sprintf("import '%s' is not allowed from list '%s'", rawBasicLit(imp.Path), l.name), + } + if sugg != "" { + diag.Message = fmt.Sprintf("%s: %s", diag.Message, sugg) + diag.SuggestedFixes = append(diag.SuggestedFixes, analysis.SuggestedFix{Message: sugg}) + } + pass.Report(diag) + } + } + } + } + return nil, nil +} + +func rawBasicLit(lit *ast.BasicLit) string { + return strings.Trim(lit.Value, "\"") +} diff --git a/vendor/github.com/OpenPeeDeeP/depguard/v2/internal/utils/errors.go b/vendor/github.com/OpenPeeDeeP/depguard/v2/internal/utils/errors.go new file mode 100644 index 00000000..65325f61 --- /dev/null +++ b/vendor/github.com/OpenPeeDeeP/depguard/v2/internal/utils/errors.go @@ -0,0 +1,18 @@ +package utils + +import ( + "strings" +) + +type MultiError []error + +func (me MultiError) Error() string { + b := strings.Builder{} + for i, e := range me { + b.WriteString(e.Error()) + if i < len(me)-1 { + b.WriteByte('\n') + } + } + return b.String() +} diff --git a/vendor/github.com/OpenPeeDeeP/depguard/v2/internal/utils/variables.go b/vendor/github.com/OpenPeeDeeP/depguard/v2/internal/utils/variables.go new file mode 100644 index 00000000..eb029b2f --- /dev/null +++ b/vendor/github.com/OpenPeeDeeP/depguard/v2/internal/utils/variables.go @@ -0,0 +1,110 @@ +package utils + +import ( + "fmt" + "go/build" + "os" + "path" +) + +type Expander interface { + Expand() ([]string, error) +} + +type ExpanderMap map[string]Expander + +var ( + PathExpandable = ExpanderMap{ + "$all": &allExpander{}, + "$test": &testExpander{}, + } + PackageExpandable = ExpanderMap{ + "$gostd": &gostdExpander{}, + } +) + +type allExpander struct{} + +func (*allExpander) Expand() ([]string, error) { + return []string{"**/*.go"}, nil +} + +type testExpander struct{} + +func (*testExpander) Expand() ([]string, error) { + return []string{"**/*_test.go"}, nil +} + +type gostdExpander struct { + cache []string +} + +// We can do this as all imports that are not root are either prefixed with a domain +// or prefixed with `./` or `/` to dictate it is a local file reference +func (e *gostdExpander) Expand() ([]string, error) { + if len(e.cache) != 0 { + return e.cache, nil + } + root := path.Join(build.Default.GOROOT, "src") + fs, err := os.ReadDir(root) + if err != nil { + return nil, fmt.Errorf("could not read GOROOT directory: %w", err) + } + var pkgPrefix []string + for _, f := range fs { + if !f.IsDir() { + continue + } + pkgPrefix = append(pkgPrefix, f.Name()) + } + e.cache = pkgPrefix + return pkgPrefix, nil +} + +func ExpandSlice(sl []string, exp ExpanderMap) ([]string, error) { + for i, s := range sl { + f, found := exp[s] + if !found { + continue + } + e, err := f.Expand() + if err != nil { + return nil, fmt.Errorf("couldn't expand %s: %w", s, err) + } + sl = insertSlice(sl, i, e...) + } + return sl, nil +} + +func ExpandMap(m map[string]string, exp ExpanderMap) error { + for k, v := range m { + f, found := exp[k] + if !found { + continue + } + e, err := f.Expand() + if err != nil { + return fmt.Errorf("couldn't expand %s: %w", k, err) + } + for _, ex := range e { + m[ex] = v + } + delete(m, k) + } + return nil +} + +func insertSlice(a []string, k int, b ...string) []string { + n := len(a) + len(b) - 1 + if n <= cap(a) { + a2 := a[:n] + copy(a2[k+len(b):], a[k+1:]) + copy(a2[k:], b) + return a2 + } + a2 := make([]string, n) + copy(a2, a[:k]) + copy(a2[k:], b) + copy(a2[k+len(b):], a[k+1:]) + return a2 +} diff --git a/vendor/github.com/OpenPeeDeeP/depguard/v2/settings.go b/vendor/github.com/OpenPeeDeeP/depguard/v2/settings.go new file mode 100644 index 00000000..440f3298 --- /dev/null +++ b/vendor/github.com/OpenPeeDeeP/depguard/v2/settings.go @@ -0,0 +1,207 @@ +package depguard + +import ( + "errors" + "fmt" + "sort" + "strings" + + "github.com/OpenPeeDeeP/depguard/v2/internal/utils" + "github.com/gobwas/glob" +) + +type List struct { + Files []string `json:"files" yaml:"files" toml:"files" mapstructure:"files"` + Allow []string `json:"allow" yaml:"allow" toml:"allow" mapstructure:"allow"` + Deny map[string]string `json:"deny" yaml:"deny" toml:"deny" mapstructure:"deny"` +} + +type list struct { + name string + files []glob.Glob + negFiles []glob.Glob + allow []string + deny []string + suggestions []string +} + +func (l *List) compile() (*list, error) { + if l == nil { + return nil, nil + } + li := &list{} + var errs utils.MultiError + var err error + + // Compile Files + for _, f := range l.Files { + var negate bool + if len(f) > 0 && f[0] == '!' { + negate = true + f = f[1:] + } + // Expand File if needed + fs, err := utils.ExpandSlice([]string{f}, utils.PathExpandable) + if err != nil { + errs = append(errs, err) + } + for _, exp := range fs { + g, err := glob.Compile(exp, '/') + if err != nil { + errs = append(errs, fmt.Errorf("%s could not be compiled: %w", exp, err)) + continue + } + if negate { + li.negFiles = append(li.negFiles, g) + continue + } + li.files = append(li.files, g) + } + } + + if len(l.Allow) > 0 { + // Expand Allow + l.Allow, err = utils.ExpandSlice(l.Allow, utils.PackageExpandable) + if err != nil { + errs = append(errs, err) + } + + // Sort Allow + li.allow = make([]string, len(l.Allow)) + copy(li.allow, l.Allow) + sort.Strings(li.allow) + } + + if l.Deny != nil { + // Expand Deny Map (to keep suggestions) + err = utils.ExpandMap(l.Deny, utils.PackageExpandable) + if err != nil { + errs = append(errs, err) + } + + // Split Deny Into Package Slice + li.deny = make([]string, 0, len(l.Deny)) + for pkg := range l.Deny { + li.deny = append(li.deny, pkg) + } + + // Sort Deny + sort.Strings(li.deny) + + // Populate Suggestions to match the Deny order + li.suggestions = make([]string, 0, len(li.deny)) + for _, dp := range li.deny { + li.suggestions = append(li.suggestions, strings.TrimSpace(l.Deny[dp])) + } + } + + // Populate the type of this list + if len(li.allow) == 0 && len(li.deny) == 0 { + errs = append(errs, errors.New("must have an Allow and/or Deny package list")) + } + + if len(errs) > 0 { + return nil, errs + } + return li, nil +} + +func (l *list) fileMatch(fileName string) bool { + inAllowed := len(l.files) == 0 || strInGlobList(fileName, l.files) + inDenied := strInGlobList(fileName, l.negFiles) + return inAllowed && !inDenied +} + +func (l *list) importAllowed(imp string) (bool, string) { + inAllowed := len(l.allow) == 0 + if !inAllowed { + inAllowed, _ = strInPrefixList(imp, l.allow) + } + inDenied, suggIdx := strInPrefixList(imp, l.deny) + sugg := "" + if inDenied && suggIdx != -1 { + sugg = l.suggestions[suggIdx] + } + return inAllowed && !inDenied, sugg +} + +type LinterSettings map[string]*List + +type linterSettings []*list + +func (l LinterSettings) compile() (linterSettings, error) { + if len(l) == 0 { + // Only allow $gostd in all files + set := &List{ + Files: []string{"$all"}, + Allow: []string{"$gostd"}, + } + li, err := set.compile() + if err != nil { + return nil, err + } + li.name = "Main" + return linterSettings{li}, nil + } + names := make([]string, 0, len(l)) + for name := range l { + names = append(names, name) + } + sort.Strings(names) + li := make(linterSettings, 0, len(l)) + var errs utils.MultiError + for _, name := range names { + c, err := l[name].compile() + if err != nil { + errs = append(errs, err) + continue + } + if c == nil { + continue + } + c.name = name + li = append(li, c) + } + if len(errs) > 0 { + return nil, errs + } + + return li, nil +} + +func (ls linterSettings) whichLists(fileName string) []*list { + var matches []*list + for _, l := range ls { + if l.fileMatch(fileName) { + matches = append(matches, l) + } + } + return matches +} + +func strInGlobList(str string, globList []glob.Glob) bool { + for _, g := range globList { + if g.Match(str) { + return true + } + } + return false +} + +func strInPrefixList(str string, prefixList []string) (bool, int) { + // Idx represents where in the prefix slice the passed in string would go + // when sorted. -1 Just means that it would be at the very front of the slice. + idx := sort.Search(len(prefixList), func(i int) bool { + return strings.TrimRight(prefixList[i], "$") > str + }) - 1 + // This means that the string passed in has no way to be prefixed by anything + // in the prefix list as it is already smaller then everything + if idx == -1 { + return false, idx + } + ioc := prefixList[idx] + if ioc[len(ioc)-1] == '$' { + return str == ioc[:len(ioc)-1], idx + } + return strings.HasPrefix(str, prefixList[idx]), idx +} diff --git a/vendor/github.com/alexkohler/nakedret/v2/.gitignore b/vendor/github.com/alexkohler/nakedret/v2/.gitignore new file mode 100644 index 00000000..b4822913 --- /dev/null +++ b/vendor/github.com/alexkohler/nakedret/v2/.gitignore @@ -0,0 +1,8 @@ +# editor specific +.vscode + +# binary +/nakedret + +# usage video for docs +.github/images diff --git a/vendor/github.com/alexkohler/nakedret/v2/LICENSE b/vendor/github.com/alexkohler/nakedret/v2/LICENSE new file mode 100644 index 00000000..9310fbcf --- /dev/null +++ b/vendor/github.com/alexkohler/nakedret/v2/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Alex Kohler + +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/vendor/github.com/alexkohler/nakedret/v2/README.md b/vendor/github.com/alexkohler/nakedret/v2/README.md new file mode 100644 index 00000000..e30a0cde --- /dev/null +++ b/vendor/github.com/alexkohler/nakedret/v2/README.md @@ -0,0 +1,125 @@ +# nakedret + +nakedret is a Go static analysis tool to find naked returns in functions greater than a specified function length. + +## Installation +Install Nakedret via go install: + +```cmd +go install github.com/alexkohler/nakedret/cmd/nakedret@latest +``` + +If you have not already added your `GOPATH/bin` directory to your `PATH` environment variable then you will need to do so. + +Windows (cmd): +```cmd +set PATH=%PATH%;C:\your\GOPATH\bin +``` + +Bash (you can verify a path has been set): +```Bash +# Check if nakedret is on PATH +which nakedret +export PATH=$PATH:/your/GOPATH/bin #to set path if it does not exist +``` + +## Usage + +Similar to other Go static anaylsis tools (such as `golint`, `go vet`), nakedret can be invoked with one or more filenames, directories, or packages named by its import path. Nakedret also supports the `...` wildcard. + + nakedret [flags] files/directories/packages + +Currently, the only flag supported is -l, which is an optional numeric flag to specify the maximum length a function can be (in terms of line length). If not specified, it defaults to 5. + +It can also be run using `go vet`: + +```shell +go vet -vettool=$(which nakedret) ./... +``` + +## Purpose + +As noted in Go's [Code Review comments](https://github.com/golang/go/wiki/CodeReviewComments#named-result-parameters): + +> Naked returns are okay if the function is a handful of lines. Once it's a medium sized function, be explicit with your return +> values. Corollary: it's not worth it to name result parameters just because it enables you to use naked returns. Clarity of docs is always more important than saving a line or two in your function. + +This tool aims to catch naked returns on non-trivial functions. + +## Example + +Let's take the `types` package in the Go source as an example: + +```Bash +$ nakedret -l 25 types/ +types/check.go:245 checkFiles naked returns on 26 line function +types/typexpr.go:443 collectParams naked returns on 53 line function +types/stmt.go:275 caseTypes naked returns on 27 line function +types/lookup.go:275 MissingMethod naked returns on 39 line function +``` + +Below is one of the not so intuitive uses of naked returns in `types/lookup.go` found by nakedret (nakedret will return the line number of the last naked return in the function): + + +```Go +func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType bool) { + // fast path for common case + if T.Empty() { + return + } + + // TODO(gri) Consider using method sets here. Might be more efficient. + + if ityp, _ := V.Underlying().(*Interface); ityp != nil { + // TODO(gri) allMethods is sorted - can do this more efficiently + for _, m := range T.allMethods { + _, obj := lookupMethod(ityp.allMethods, m.pkg, m.name) + switch { + case obj == nil: + if static { + return m, false + } + case !Identical(obj.Type(), m.typ): + return m, true + } + } + return + } + + // A concrete type implements T if it implements all methods of T. + for _, m := range T.allMethods { + obj, _, _ := lookupFieldOrMethod(V, false, m.pkg, m.name) + + f, _ := obj.(*Func) + if f == nil { + return m, false + } + + if !Identical(f.typ, m.typ) { + return m, true + } + } + + return +} +``` + +## TODO + +- Unit tests (may require some refactoring to do correctly) +- supporting toggling of `build.Context.UseAllFiles` may be useful for some. +- Configuration on whether or not to run on test files +- Vim quickfix format? + + +## Contributing + +Pull requests welcome! + + +## Other static analysis tools + +If you've enjoyed nakedret, take a look at my other static anaylsis tools! + +- [unimport](https://github.com/alexkohler/unimport) - Finds unnecessary import aliases +- [prealloc](https://github.com/alexkohler/prealloc) - Finds slice declarations that could potentially be preallocated. diff --git a/vendor/github.com/alexkohler/nakedret/v2/import.go b/vendor/github.com/alexkohler/nakedret/v2/import.go new file mode 100644 index 00000000..dea84233 --- /dev/null +++ b/vendor/github.com/alexkohler/nakedret/v2/import.go @@ -0,0 +1,310 @@ +package nakedret + +/* + +This file holds a direct copy of the import path matching code of +https://github.com/golang/go/blob/master/src/cmd/go/main.go. It can be +replaced when https://golang.org/issue/8768 is resolved. + +It has been updated to follow upstream changes in a few ways. + +*/ + +import ( + "fmt" + "go/build" + "log" + "os" + "path" + "path/filepath" + "regexp" + "runtime" + "strings" +) + +var buildContext = build.Default + +var ( + goroot = filepath.Clean(runtime.GOROOT()) + gorootSrc = filepath.Join(goroot, "src") +) + +// importPathsNoDotExpansion returns the import paths to use for the given +// command line, but it does no ... expansion. +func importPathsNoDotExpansion(args []string) []string { + if len(args) == 0 { + return []string{"."} + } + var out []string + for _, a := range args { + // Arguments are supposed to be import paths, but + // as a courtesy to Windows developers, rewrite \ to / + // in command-line arguments. Handles .\... and so on. + if filepath.Separator == '\\' { + a = strings.Replace(a, `\`, `/`, -1) + } + + // Put argument in canonical form, but preserve leading ./. + if strings.HasPrefix(a, "./") { + a = "./" + path.Clean(a) + if a == "./." { + a = "." + } + } else { + a = path.Clean(a) + } + if a == "all" || a == "std" { + out = append(out, allPackages(a)...) + continue + } + out = append(out, a) + } + return out +} + +// importPaths returns the import paths to use for the given command line. +func importPaths(args []string) []string { + args = importPathsNoDotExpansion(args) + var out []string + for _, a := range args { + if strings.Contains(a, "...") { + if build.IsLocalImport(a) { + out = append(out, allPackagesInFS(a)...) + } else { + out = append(out, allPackages(a)...) + } + continue + } + out = append(out, a) + } + return out +} + +// matchPattern(pattern)(name) reports whether +// name matches pattern. Pattern is a limited glob +// pattern in which '...' means 'any string' and there +// is no other special syntax. +func matchPattern(pattern string) func(name string) bool { + re := regexp.QuoteMeta(pattern) + re = strings.Replace(re, `\.\.\.`, `.*`, -1) + // Special case: foo/... matches foo too. + if strings.HasSuffix(re, `/.*`) { + re = re[:len(re)-len(`/.*`)] + `(/.*)?` + } + reg := regexp.MustCompile(`^` + re + `$`) + return func(name string) bool { + return reg.MatchString(name) + } +} + +// hasPathPrefix reports whether the path s begins with the +// elements in prefix. +func hasPathPrefix(s, prefix string) bool { + switch { + default: + return false + case len(s) == len(prefix): + return s == prefix + case len(s) > len(prefix): + if prefix != "" && prefix[len(prefix)-1] == '/' { + return strings.HasPrefix(s, prefix) + } + return s[len(prefix)] == '/' && s[:len(prefix)] == prefix + } +} + +// treeCanMatchPattern(pattern)(name) reports whether +// name or children of name can possibly match pattern. +// Pattern is the same limited glob accepted by matchPattern. +func treeCanMatchPattern(pattern string) func(name string) bool { + wildCard := false + if i := strings.Index(pattern, "..."); i >= 0 { + wildCard = true + pattern = pattern[:i] + } + return func(name string) bool { + return len(name) <= len(pattern) && hasPathPrefix(pattern, name) || + wildCard && strings.HasPrefix(name, pattern) + } +} + +// allPackages returns all the packages that can be found +// under the $GOPATH directories and $GOROOT matching pattern. +// The pattern is either "all" (all packages), "std" (standard packages) +// or a path including "...". +func allPackages(pattern string) []string { + pkgs := matchPackages(pattern) + if len(pkgs) == 0 { + fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) + } + return pkgs +} + +func matchPackages(pattern string) []string { + match := func(string) bool { return true } + treeCanMatch := func(string) bool { return true } + if pattern != "all" && pattern != "std" { + match = matchPattern(pattern) + treeCanMatch = treeCanMatchPattern(pattern) + } + + have := map[string]bool{ + "builtin": true, // ignore pseudo-package that exists only for documentation + } + if !buildContext.CgoEnabled { + have["runtime/cgo"] = true // ignore during walk + } + var pkgs []string + + // Commands + cmd := filepath.Join(goroot, "src/cmd") + string(filepath.Separator) + filepath.Walk(cmd, func(path string, fi os.FileInfo, err error) error { + if err != nil || !fi.IsDir() || path == cmd { + return nil + } + name := path[len(cmd):] + if !treeCanMatch(name) { + return filepath.SkipDir + } + // Commands are all in cmd/, not in subdirectories. + if strings.Contains(name, string(filepath.Separator)) { + return filepath.SkipDir + } + + // We use, e.g., cmd/gofmt as the pseudo import path for gofmt. + name = "cmd/" + name + if have[name] { + return nil + } + have[name] = true + if !match(name) { + return nil + } + _, err = buildContext.ImportDir(path, 0) + if err != nil { + if _, noGo := err.(*build.NoGoError); !noGo { + log.Print(err) + } + return nil + } + pkgs = append(pkgs, name) + return nil + }) + + for _, src := range buildContext.SrcDirs() { + if (pattern == "std" || pattern == "cmd") && src != gorootSrc { + continue + } + src = filepath.Clean(src) + string(filepath.Separator) + root := src + if pattern == "cmd" { + root += "cmd" + string(filepath.Separator) + } + filepath.Walk(root, func(path string, fi os.FileInfo, err error) error { + if err != nil || !fi.IsDir() || path == src { + return nil + } + + // Avoid .foo, _foo, testdata and vendor directory trees. + _, elem := filepath.Split(path) + if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" || elem == "vendor" { + return filepath.SkipDir + } + + name := filepath.ToSlash(path[len(src):]) + if pattern == "std" && (strings.Contains(name, ".") || name == "cmd") { + // The name "std" is only the standard library. + // If the name is cmd, it's the root of the command tree. + return filepath.SkipDir + } + if !treeCanMatch(name) { + return filepath.SkipDir + } + if have[name] { + return nil + } + have[name] = true + if !match(name) { + return nil + } + _, err = buildContext.ImportDir(path, 0) + if err != nil { + if _, noGo := err.(*build.NoGoError); noGo { + return nil + } + } + pkgs = append(pkgs, name) + return nil + }) + } + return pkgs +} + +// allPackagesInFS is like allPackages but is passed a pattern +// beginning ./ or ../, meaning it should scan the tree rooted +// at the given directory. There are ... in the pattern too. +func allPackagesInFS(pattern string) []string { + pkgs := matchPackagesInFS(pattern) + if len(pkgs) == 0 { + fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) + } + return pkgs +} + +func matchPackagesInFS(pattern string) []string { + // Find directory to begin the scan. + // Could be smarter but this one optimization + // is enough for now, since ... is usually at the + // end of a path. + i := strings.Index(pattern, "...") + dir, _ := path.Split(pattern[:i]) + + // pattern begins with ./ or ../. + // path.Clean will discard the ./ but not the ../. + // We need to preserve the ./ for pattern matching + // and in the returned import paths. + prefix := "" + if strings.HasPrefix(pattern, "./") { + prefix = "./" + } + match := matchPattern(pattern) + + var pkgs []string + filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error { + if err != nil || !fi.IsDir() { + return nil + } + if path == dir { + // filepath.Walk starts at dir and recurses. For the recursive case, + // the path is the result of filepath.Join, which calls filepath.Clean. + // The initial case is not Cleaned, though, so we do this explicitly. + // + // This converts a path like "./io/" to "io". Without this step, running + // "cd $GOROOT/src/pkg; go list ./io/..." would incorrectly skip the io + // package, because prepending the prefix "./" to the unclean path would + // result in "././io", and match("././io") returns false. + path = filepath.Clean(path) + } + + // Avoid .foo, _foo, testdata and vendor directory trees, but do not avoid "." or "..". + _, elem := filepath.Split(path) + dot := strings.HasPrefix(elem, ".") && elem != "." && elem != ".." + if dot || strings.HasPrefix(elem, "_") || elem == "testdata" || elem == "vendor" { + return filepath.SkipDir + } + + name := prefix + filepath.ToSlash(path) + if !match(name) { + return nil + } + if _, err = build.ImportDir(path, 0); err != nil { + if _, noGo := err.(*build.NoGoError); !noGo { + log.Print(err) + } + return nil + } + pkgs = append(pkgs, name) + return nil + }) + return pkgs +} diff --git a/vendor/github.com/alexkohler/nakedret/v2/nakedret.go b/vendor/github.com/alexkohler/nakedret/v2/nakedret.go new file mode 100644 index 00000000..41979c31 --- /dev/null +++ b/vendor/github.com/alexkohler/nakedret/v2/nakedret.go @@ -0,0 +1,316 @@ +package nakedret + +import ( + "bytes" + "errors" + "flag" + "fmt" + "go/ast" + "go/build" + "go/parser" + "go/printer" + "go/token" + "log" + "os" + "path/filepath" + "strings" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/inspector" +) + +const ( + pwd = "./" +) + +func init() { + //TODO allow build tags + build.Default.UseAllFiles = true +} + +func NakedReturnAnalyzer(defaultLines uint) *analysis.Analyzer { + nakedRet := &NakedReturnRunner{} + flags := flag.NewFlagSet("nakedret", flag.ExitOnError) + flags.UintVar(&nakedRet.MaxLength, "l", defaultLines, "maximum number of lines for a naked return function") + var analyzer = &analysis.Analyzer{ + Name: "nakedret", + Doc: "Checks that functions with naked returns are not longer than a maximum size (can be zero).", + Run: nakedRet.run, + Flags: *flags, + Requires: []*analysis.Analyzer{inspect.Analyzer}, + } + return analyzer +} + +type NakedReturnRunner struct { + MaxLength uint +} + +func (n *NakedReturnRunner) run(pass *analysis.Pass) (any, error) { + inspector := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) + + nodeFilter := []ast.Node{ // filter needed nodes: visit only them + (*ast.FuncDecl)(nil), + (*ast.FuncLit)(nil), + (*ast.ReturnStmt)(nil), + } + retVis := &returnsVisitor{ + pass: pass, + f: pass.Fset, + maxLength: n.MaxLength, + } + inspector.Nodes(nodeFilter, retVis.NodesVisit) + return nil, nil +} + +type returnsVisitor struct { + pass *analysis.Pass + f *token.FileSet + maxLength uint + + // functions contains funcInfo for each nested function definition encountered while visiting the AST. + functions []funcInfo +} + +type funcInfo struct { + // Details of the function we're currently dealing with + funcType *ast.FuncType + funcName string + funcLength int + reportNaked bool +} + +func checkNakedReturns(args []string, maxLength *uint, setExitStatus bool) error { + + fset := token.NewFileSet() + + files, err := parseInput(args, fset) + if err != nil { + return fmt.Errorf("could not parse input: %v", err) + } + + if maxLength == nil { + return errors.New("max length nil") + } + + analyzer := NakedReturnAnalyzer(*maxLength) + pass := &analysis.Pass{ + Analyzer: analyzer, + Fset: fset, + Files: files, + Report: func(d analysis.Diagnostic) { + log.Printf("%s:%d: %s", fset.Position(d.Pos).Filename, fset.Position(d.Pos).Line, d.Message) + }, + ResultOf: map[*analysis.Analyzer]any{}, + } + result, err := inspect.Analyzer.Run(pass) + if err != nil { + return err + } + pass.ResultOf[inspect.Analyzer] = result + + _, err = analyzer.Run(pass) + if err != nil { + return err + } + + return nil +} + +func parseInput(args []string, fset *token.FileSet) ([]*ast.File, error) { + var directoryList []string + var fileMode bool + files := make([]*ast.File, 0) + + if len(args) == 0 { + directoryList = append(directoryList, pwd) + } else { + for _, arg := range args { + if strings.HasSuffix(arg, "/...") && isDir(arg[:len(arg)-len("/...")]) { + + for _, dirname := range allPackagesInFS(arg) { + directoryList = append(directoryList, dirname) + } + + } else if isDir(arg) { + directoryList = append(directoryList, arg) + + } else if exists(arg) { + if strings.HasSuffix(arg, ".go") { + fileMode = true + f, err := parser.ParseFile(fset, arg, nil, 0) + if err != nil { + return nil, err + } + files = append(files, f) + } else { + return nil, fmt.Errorf("invalid file %v specified", arg) + } + } else { + + //TODO clean this up a bit + imPaths := importPaths([]string{arg}) + for _, importPath := range imPaths { + pkg, err := build.Import(importPath, ".", 0) + if err != nil { + return nil, err + } + var stringFiles []string + stringFiles = append(stringFiles, pkg.GoFiles...) + // files = append(files, pkg.CgoFiles...) + stringFiles = append(stringFiles, pkg.TestGoFiles...) + if pkg.Dir != "." { + for i, f := range stringFiles { + stringFiles[i] = filepath.Join(pkg.Dir, f) + } + } + + fileMode = true + for _, stringFile := range stringFiles { + f, err := parser.ParseFile(fset, stringFile, nil, 0) + if err != nil { + return nil, err + } + files = append(files, f) + } + + } + } + } + } + + // if we're not in file mode, then we need to grab each and every package in each directory + // we can to grab all the files + if !fileMode { + for _, fpath := range directoryList { + pkgs, err := parser.ParseDir(fset, fpath, nil, 0) + if err != nil { + return nil, err + } + + for _, pkg := range pkgs { + for _, f := range pkg.Files { + files = append(files, f) + } + } + } + } + + return files, nil +} + +func isDir(filename string) bool { + fi, err := os.Stat(filename) + return err == nil && fi.IsDir() +} + +func exists(filename string) bool { + _, err := os.Stat(filename) + return err == nil +} + +func hasNamedReturns(funcType *ast.FuncType) bool { + if funcType == nil || funcType.Results == nil { + return false + } + for _, field := range funcType.Results.List { + for _, ident := range field.Names { + if ident != nil { + return true + } + } + } + return false +} + +func nestedFuncName(functions []funcInfo) string { + var names []string + for _, f := range functions { + names = append(names, f.funcName) + } + return strings.Join(names, ".") +} + +func nakedReturnFix(s *ast.ReturnStmt, funcType *ast.FuncType) *ast.ReturnStmt { + var nameExprs []ast.Expr + for _, result := range funcType.Results.List { + for _, ident := range result.Names { + if ident != nil { + nameExprs = append(nameExprs, ident) + } + } + } + var sFix = *s + sFix.Results = nameExprs + return &sFix +} + +func (v *returnsVisitor) NodesVisit(node ast.Node, push bool) bool { + var ( + funcType *ast.FuncType + funcName string + ) + switch s := node.(type) { + case *ast.FuncDecl: + // We've found a function + funcType = s.Type + funcName = s.Name.Name + case *ast.FuncLit: + // We've found a function literal + funcType = s.Type + file := v.f.File(s.Pos()) + funcName = fmt.Sprintf("", file.Position(s.Pos()).Line) + case *ast.ReturnStmt: + // We've found a possibly naked return statement + fun := v.functions[len(v.functions)-1] + funName := nestedFuncName(v.functions) + if fun.reportNaked && len(s.Results) == 0 && push { + sFix := nakedReturnFix(s, fun.funcType) + b := &bytes.Buffer{} + err := printer.Fprint(b, v.f, sFix) + if err != nil { + log.Printf("failed to format named return fix: %s", err) + } + v.pass.Report(analysis.Diagnostic{ + Pos: s.Pos(), + End: s.End(), + Message: fmt.Sprintf("naked return in func `%s` with %d lines of code", funName, fun.funcLength), + SuggestedFixes: []analysis.SuggestedFix{{ + Message: "explicit return statement", + TextEdits: []analysis.TextEdit{{ + Pos: s.Pos(), + End: s.End(), + NewText: b.Bytes()}}, + }}, + }) + } + } + + if !push { + if funcType == nil { + return false + } + // Pop function info + v.functions = v.functions[:len(v.functions)-1] + return false + } + + if push && funcType != nil { + // Push function info to track returns for this function + file := v.f.File(node.Pos()) + length := file.Position(node.End()).Line - file.Position(node.Pos()).Line + if length == 0 { + // consider functions that finish on the same line as they start as single line functions, not zero lines! + length = 1 + } + v.functions = append(v.functions, funcInfo{ + funcType: funcType, + funcName: funcName, + funcLength: length, + reportNaked: uint(length) > v.maxLength && hasNamedReturns(funcType), + }) + } + + return true +} diff --git a/vendor/github.com/butuzov/ireturn/analyzer/analyzer.go b/vendor/github.com/butuzov/ireturn/analyzer/analyzer.go index f4fdaaed..3a0bf740 100644 --- a/vendor/github.com/butuzov/ireturn/analyzer/analyzer.go +++ b/vendor/github.com/butuzov/ireturn/analyzer/analyzer.go @@ -2,14 +2,13 @@ package analyzer import ( "flag" - "fmt" "go/ast" gotypes "go/types" "strings" "sync" - "github.com/butuzov/ireturn/config" - "github.com/butuzov/ireturn/types" + "github.com/butuzov/ireturn/analyzer/internal/config" + "github.com/butuzov/ireturn/analyzer/internal/types" "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis/passes/inspect" @@ -39,8 +38,18 @@ func (a *analyzer) run(pass *analysis.Pass) (interface{}, error) { return nil, a.err } - // 01. Running Inspection. ins, _ := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) + + // 00. does file have dot-imported standard packages? + dotImportedStd := make(map[string]struct{}) + ins.Preorder([]ast.Node{(*ast.ImportSpec)(nil)}, func(node ast.Node) { + i, _ := node.(*ast.ImportSpec) + if i.Name != nil && i.Name.Name == "." { + dotImportedStd[strings.Trim(i.Path.Value, `"`)] = struct{}{} + } + }) + + // 01. Running Inspection. ins.Preorder([]ast.Node{(*ast.FuncDecl)(nil)}, func(node ast.Node) { // 001. Casting to funcdecl f, _ := node.(*ast.FuncDecl) @@ -56,17 +65,25 @@ func (a *analyzer) run(pass *analysis.Pass) (interface{}, error) { return } + seen := make(map[string]bool, 4) + // 004. Filtering Results. - for _, i := range filterInterfaces(pass, f.Type.Results) { + for _, issue := range filterInterfaces(pass, f.Type, dotImportedStd) { + + if a.handler.IsValid(issue) { + continue + } + + issue.Enrich(f) + + key := issue.HashString() - if a.handler.IsValid(i) { + if ok := seen[key]; ok { continue } + seen[key] = true - a.found = append(a.found, analysis.Diagnostic{ //nolint: exhaustivestruct - Pos: f.Pos(), - Message: fmt.Sprintf("%s returns interface (%s)", f.Name.Name, i.Name), - }) + a.found = append(a.found, issue.ExportDiagnostic()) } }) @@ -112,25 +129,31 @@ func flags() flag.FlagSet { return *set } -func filterInterfaces(pass *analysis.Pass, fl *ast.FieldList) []types.IFace { +func filterInterfaces(p *analysis.Pass, ft *ast.FuncType, di map[string]struct{}) []types.IFace { var results []types.IFace - for pos, el := range fl.List { + if ft.Results == nil { // this can't happen, but double checking. + return results + } + + tp := newTypeParams(ft.TypeParams) + + for _, el := range ft.Results.List { switch v := el.Type.(type) { // ----- empty or anonymous interfaces case *ast.InterfaceType: if len(v.Methods.List) == 0 { - results = append(results, issue("interface{}", pos, types.EmptyInterface)) + results = append(results, types.NewIssue("interface{}", types.EmptyInterface)) continue } - results = append(results, issue("anonymous interface", pos, types.AnonInterface)) + results = append(results, types.NewIssue("anonymous interface", types.AnonInterface)) // ------ Errors and interfaces from same package case *ast.Ident: - t1 := pass.TypesInfo.TypeOf(el.Type) + t1 := p.TypesInfo.TypeOf(el.Type) if !gotypes.IsInterface(t1.Underlying()) { continue } @@ -138,56 +161,72 @@ func filterInterfaces(pass *analysis.Pass, fl *ast.FieldList) []types.IFace { word := t1.String() // only build in interface is error if obj := gotypes.Universe.Lookup(word); obj != nil { - results = append(results, issue(obj.Name(), pos, types.ErrorInterface)) + results = append(results, types.NewIssue(obj.Name(), types.ErrorInterface)) + continue + } + // found in type params + if tp.In(word) { + results = append(results, types.NewIssue(word, types.Generic)) continue } - results = append(results, issue(word, pos, types.NamedInterface)) + // is it dot-imported package? + // handling cases when stdlib package imported via "." dot-import + if len(di) > 0 { + name := stdPkgInterface(word) + if _, ok := di[name]; ok { + results = append(results, types.NewIssue(word, types.NamedStdInterface)) + + continue + } + } + + results = append(results, types.NewIssue(word, types.NamedInterface)) // ------- standard library and 3rd party interfaces case *ast.SelectorExpr: - t1 := pass.TypesInfo.TypeOf(el.Type) + t1 := p.TypesInfo.TypeOf(el.Type) if !gotypes.IsInterface(t1.Underlying()) { continue } word := t1.String() - if isStdLib(word) { - results = append(results, issue(word, pos, types.NamedStdInterface)) - + if isStdPkgInterface(word) { + results = append(results, types.NewIssue(word, types.NamedStdInterface)) continue } - results = append(results, issue(word, pos, types.NamedInterface)) + results = append(results, types.NewIssue(word, types.NamedInterface)) } } return results } -// isStdLib will run small checks against pkg to find out if named interface -// we lookling on comes from a standard library or not. -func isStdLib(named string) bool { - // find last dot index. +// stdPkgInterface will return package name if tis std lib package +// or empty string on fail. +func stdPkgInterface(named string) string { + // find last "." index. idx := strings.LastIndex(named, ".") if idx == -1 { - return false + return "" } - if _, ok := std[named[0:idx]]; ok { - return true - } + return stdPkg(named[0:idx]) +} - return false +// isStdPkgInterface will run small checks against pkg to find out if named +// interface we looking on - comes from a standard library or not. +func isStdPkgInterface(namedInterface string) bool { + return stdPkgInterface(namedInterface) != "" } -// issue is shortcut that creates issue for next filtering. -func issue(name string, pos int, interfaceType types.IType) types.IFace { - return types.IFace{ - Name: name, - Pos: pos, - Type: interfaceType, +func stdPkg(pkg string) string { + if _, ok := std[pkg]; ok { + return pkg } + + return "" } diff --git a/vendor/github.com/butuzov/ireturn/config/allow.go b/vendor/github.com/butuzov/ireturn/analyzer/internal/config/allow.go similarity index 87% rename from vendor/github.com/butuzov/ireturn/config/allow.go rename to vendor/github.com/butuzov/ireturn/analyzer/internal/config/allow.go index c171a255..6a294ca3 100644 --- a/vendor/github.com/butuzov/ireturn/config/allow.go +++ b/vendor/github.com/butuzov/ireturn/analyzer/internal/config/allow.go @@ -1,6 +1,6 @@ package config -import "github.com/butuzov/ireturn/types" +import "github.com/butuzov/ireturn/analyzer/internal/types" // allowConfig specifies a list of interfaces (keywords, patters and regular expressions) // that are allowed by ireturn as valid to return, any non listed interface are rejected. diff --git a/vendor/github.com/butuzov/ireturn/config/config.go b/vendor/github.com/butuzov/ireturn/analyzer/internal/config/config.go similarity index 92% rename from vendor/github.com/butuzov/ireturn/config/config.go rename to vendor/github.com/butuzov/ireturn/analyzer/internal/config/config.go index 7307ab3e..e2f1aef6 100644 --- a/vendor/github.com/butuzov/ireturn/config/config.go +++ b/vendor/github.com/butuzov/ireturn/analyzer/internal/config/config.go @@ -3,7 +3,7 @@ package config import ( "regexp" - "github.com/butuzov/ireturn/types" + "github.com/butuzov/ireturn/analyzer/internal/types" ) // defaultConfig is core of the validation, ... @@ -55,6 +55,8 @@ func (config *defaultConfig) compileList() { config.quick |= uint8(types.AnonInterface) case types.NameStdLib: config.quick |= uint8(types.NamedStdInterface) + case types.NameGeneric: + config.quick |= uint8(types.Generic) } // allow to parse regular expressions diff --git a/vendor/github.com/butuzov/ireturn/config/new.go b/vendor/github.com/butuzov/ireturn/analyzer/internal/config/new.go similarity index 92% rename from vendor/github.com/butuzov/ireturn/config/new.go rename to vendor/github.com/butuzov/ireturn/analyzer/internal/config/new.go index cfaa274a..6aa04e52 100644 --- a/vendor/github.com/butuzov/ireturn/config/new.go +++ b/vendor/github.com/butuzov/ireturn/analyzer/internal/config/new.go @@ -5,12 +5,12 @@ import ( "flag" "strings" - "github.com/butuzov/ireturn/types" + "github.com/butuzov/ireturn/analyzer/internal/types" ) var ErrCollisionOfInterests = errors.New("can't have both `-accept` and `-reject` specified at same time") -//nolint: exhaustivestruct +// nolint: exhaustivestruct func DefaultValidatorConfig() *allowConfig { return allowAll([]string{ types.NameEmpty, // "empty": empty interfaces (interface{}) @@ -40,7 +40,7 @@ func New(fs *flag.FlagSet) (interface{}, error) { return rejectAll(rejectList), nil } - // can have none at same time. + // can have none (defaults are used) at same time. return nil, nil } diff --git a/vendor/github.com/butuzov/ireturn/config/reject.go b/vendor/github.com/butuzov/ireturn/analyzer/internal/config/reject.go similarity index 87% rename from vendor/github.com/butuzov/ireturn/config/reject.go rename to vendor/github.com/butuzov/ireturn/analyzer/internal/config/reject.go index 21e50114..bef6913b 100644 --- a/vendor/github.com/butuzov/ireturn/config/reject.go +++ b/vendor/github.com/butuzov/ireturn/analyzer/internal/config/reject.go @@ -1,6 +1,6 @@ package config -import "github.com/butuzov/ireturn/types" +import "github.com/butuzov/ireturn/analyzer/internal/types" // rejectConfig specifies a list of interfaces (keywords, patters and regular expressions) // that are rejected by ireturn as valid to return, any non listed interface are allowed. diff --git a/vendor/github.com/butuzov/ireturn/analyzer/internal/types/iface.go b/vendor/github.com/butuzov/ireturn/analyzer/internal/types/iface.go new file mode 100644 index 00000000..13f19a3e --- /dev/null +++ b/vendor/github.com/butuzov/ireturn/analyzer/internal/types/iface.go @@ -0,0 +1,49 @@ +package types + +import ( + "fmt" + "go/ast" + "go/token" + + "golang.org/x/tools/go/analysis" +) + +type IFace struct { + Name string // Interface name + Type IType // Type of the interface + + Pos token.Pos // Token Position + FuncName string // +} + +func NewIssue(name string, interfaceType IType) IFace { + return IFace{ + Name: name, + // Pos: pos, + Type: interfaceType, + } +} + +func (i *IFace) Enrich(f *ast.FuncDecl) { + i.FuncName = f.Name.Name + i.Pos = f.Pos() +} + +func (i IFace) String() string { + if i.Type == Generic { + return fmt.Sprintf("%s returns generic interface (%s)", i.FuncName, i.Name) + } + + return fmt.Sprintf("%s returns interface (%s)", i.FuncName, i.Name) +} + +func (i IFace) HashString() string { + return fmt.Sprintf("%v-%s", i.Pos, i.String()) +} + +func (i IFace) ExportDiagnostic() analysis.Diagnostic { + return analysis.Diagnostic{ //nolint: exhaustivestruct + Pos: i.Pos, + Message: i.String(), + } +} diff --git a/vendor/github.com/butuzov/ireturn/analyzer/internal/types/names.go b/vendor/github.com/butuzov/ireturn/analyzer/internal/types/names.go new file mode 100644 index 00000000..1092c966 --- /dev/null +++ b/vendor/github.com/butuzov/ireturn/analyzer/internal/types/names.go @@ -0,0 +1,9 @@ +package types + +const ( + NameEmpty = "empty" + NameAnon = "anon" + NameError = "error" + NameStdLib = "stdlib" + NameGeneric = "generic" +) diff --git a/vendor/github.com/butuzov/ireturn/types/types.go b/vendor/github.com/butuzov/ireturn/analyzer/internal/types/types.go similarity index 81% rename from vendor/github.com/butuzov/ireturn/types/types.go rename to vendor/github.com/butuzov/ireturn/analyzer/internal/types/types.go index 837570db..5c0bd740 100644 --- a/vendor/github.com/butuzov/ireturn/types/types.go +++ b/vendor/github.com/butuzov/ireturn/analyzer/internal/types/types.go @@ -8,4 +8,5 @@ const ( ErrorInterface // ref as error NamedInterface // ref as named NamedStdInterface // ref as named stdlib + Generic // ref as generic type parameter ) diff --git a/vendor/github.com/butuzov/ireturn/analyzer/std.go b/vendor/github.com/butuzov/ireturn/analyzer/std.go index 2af5284a..ec361cd4 100644 --- a/vendor/github.com/butuzov/ireturn/analyzer/std.go +++ b/vendor/github.com/butuzov/ireturn/analyzer/std.go @@ -183,4 +183,12 @@ var std = map[string]struct{}{ "runtime/metrics": {}, "testing/fstest": {}, // added in Go v1.17 in compare to v1.16 (docker image) + // added in Go v1.18 in compare to v1.17 (docker image) + "debug/buildinfo": {}, + "net/netip": {}, + // added in Go v1.19 in compare to v1.18 (docker image) + "go/doc/comment": {}, + // added in Go v1.20 in compare to v1.19 (docker image) + "crypto/ecdh": {}, + "runtime/coverage": {}, } diff --git a/vendor/github.com/butuzov/ireturn/analyzer/typeparams.go b/vendor/github.com/butuzov/ireturn/analyzer/typeparams.go new file mode 100644 index 00000000..14193c35 --- /dev/null +++ b/vendor/github.com/butuzov/ireturn/analyzer/typeparams.go @@ -0,0 +1,38 @@ +package analyzer + +import ( + "go/ast" +) + +type typeParams struct { + found []string +} + +func newTypeParams(fl *ast.FieldList) typeParams { + tp := typeParams{} + + if fl == nil { + return tp + } + + for _, el := range fl.List { + if el == nil { + continue + } + + for _, name := range el.Names { + tp.found = append(tp.found, name.Name) + } + } + + return tp +} + +func (tp typeParams) In(t string) bool { + for _, i := range tp.found { + if i == t { + return true + } + } + return false +} diff --git a/vendor/github.com/butuzov/ireturn/types/iface.go b/vendor/github.com/butuzov/ireturn/types/iface.go deleted file mode 100644 index e9baa37c..00000000 --- a/vendor/github.com/butuzov/ireturn/types/iface.go +++ /dev/null @@ -1,7 +0,0 @@ -package types - -type IFace struct { - Name string // Preserved for named interfaces - Pos int // Position in return tuple - Type IType // Type of the interface -} diff --git a/vendor/github.com/butuzov/ireturn/types/names.go b/vendor/github.com/butuzov/ireturn/types/names.go deleted file mode 100644 index 0b286c4c..00000000 --- a/vendor/github.com/butuzov/ireturn/types/names.go +++ /dev/null @@ -1,8 +0,0 @@ -package types - -const ( - NameEmpty = "empty" - NameAnon = "anon" - NameError = "error" - NameStdLib = "stdlib" -) diff --git a/vendor/github.com/butuzov/mirror/.act b/vendor/github.com/butuzov/mirror/.act new file mode 100644 index 00000000..8182d703 --- /dev/null +++ b/vendor/github.com/butuzov/mirror/.act @@ -0,0 +1,2 @@ +--platform ubuntu-latest=butuzov/act-go:latest +--env DRY_RUN=1 diff --git a/vendor/github.com/butuzov/mirror/.editorconfig b/vendor/github.com/butuzov/mirror/.editorconfig new file mode 100644 index 00000000..4d9c20d8 --- /dev/null +++ b/vendor/github.com/butuzov/mirror/.editorconfig @@ -0,0 +1,28 @@ +# top-most EditorConfig file +root = true + + +[*] +end_of_line = lf # Unix-style newlines +charset = utf-8 + +indent_style = space # default identation - spaces +indent_size = 4 # default identation - size + +insert_final_newline = true # new line at the end of file +trim_trailing_whitespace = true # no extra sapces at the end of lines + +[*.{go,gohtml,gotpl}] # Go +indent_style = tab +indent_size = 2 + +[{Makefile,makefile}] # CMake +indent_style = tab + +[*.md] # Markdown +trim_trailing_whitespace = true +max_line_length = 100 +insert_final_newline = true +indent_size = 2 + + diff --git a/vendor/github.com/butuzov/mirror/.gitignore b/vendor/github.com/butuzov/mirror/.gitignore new file mode 100644 index 00000000..109f33b9 --- /dev/null +++ b/vendor/github.com/butuzov/mirror/.gitignore @@ -0,0 +1,11 @@ +# artifacts +coverage.cov +bin/* +dist/* +tmp/* +out* +sandbox* +demo* +.task* +.ipynb* +.jupyter* diff --git a/vendor/github.com/butuzov/mirror/.goreleaser.yaml b/vendor/github.com/butuzov/mirror/.goreleaser.yaml new file mode 100644 index 00000000..11749ed2 --- /dev/null +++ b/vendor/github.com/butuzov/mirror/.goreleaser.yaml @@ -0,0 +1,61 @@ +--- +project_name: mirror + +builds: + - binary: mirror + env: + - CGO_ENABLED=0 + goos: + - darwin + - linux + - windows + goarch: + - amd64 + - 386 + - arm64 + - arm + goarm: + - 6 + ignore: + - goos: windows + goarm: 6 + - goos: windows + goarch: arm64 + - goos: linux + goarm: 6 + - goos: darwin + goarch: 386 + main: ./cmd/mirror/ + flags: + - -trimpath + ldflags: -s -w + +checksum: + name_template: 'checksums.txt' + +changelog: + sort: asc + filters: + exclude: + - '(?i)^docs?:' + - '(?i)^docs\([^:]+\):' + - '(?i)^docs\[[^:]+\]:' + - '^tests?:' + - '(?i)^dev:' + - Merge pull request + - Merge branch + +archives: + - name_template: '{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}{{ if .Mips }}_{{ .Mips }}{{ end }}' + replacements: + darwin: darwin + linux: linux + windows: windows + 386: i386 + amd64: x86_64 + format_overrides: + - goos: windows + format: zip + files: + - LICENSE + - readme.md diff --git a/vendor/github.com/butuzov/mirror/LICENSE b/vendor/github.com/butuzov/mirror/LICENSE new file mode 100644 index 00000000..a9752e97 --- /dev/null +++ b/vendor/github.com/butuzov/mirror/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Oleg Butuzov + +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/vendor/github.com/butuzov/mirror/MIRROR_FUNCS.md b/vendor/github.com/butuzov/mirror/MIRROR_FUNCS.md new file mode 100644 index 00000000..776816e5 --- /dev/null +++ b/vendor/github.com/butuzov/mirror/MIRROR_FUNCS.md @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
func (b *bufio.Writer) WriteString(s string) (int, error) + func (b *bufio.Writer) Write(p []byte) (int, error) + func (b *bufio.Writer) WriteRune(r rune) (int, error) +
func (b *bytes.Buffer) WriteString(s string) (int, error) + func (b *bytes.Buffer) Write(p []byte) (int, error) + func (b *bytes.Buffer) WriteRune(r rune) (int, error) +
func strings.Compare(a, b string) intfunc bytes.Compare(a, b []byte) int
func strings.Contains(s, substr string) boolfunc bytes.Contains(b, subslice []byte) bool
func strings.ContainsAny(s, chars string) boolfunc bytes.ContainsAny(b []byte, chars string) bool
func strings.ContainsRune(s string, r rune) boolfunc bytes.ContainsRune(b []byte, r rune) bool
func strings.Count(s, substr string) intfunc bytes.Count(s, sep []byte) int
func strings.EqualFold(s, t string) boolfunc bytes.EqualFold(s, t []byte) bool
func strings.HasPrefix(s, prefix string) boolfunc bytes.HasPrefix(s, prefix []byte) bool
func strings.HasSuffix(s, suffix string) boolfunc bytes.HasSuffix(s, suffix []byte) bool
func strings.Index(s, substr string) intfunc bytes.Index(s, sep []byte) int
func strings.IndexAny(s, chars string) intfunc bytes.IndexAny(s []byte, chars string) int
func strings.IndexByte(s string, c byte) intfunc bytes.IndexByte(b []byte, c byte) int
func strings.IndexFunc(s string, f func(rune) bool) intfunc bytes.IndexFunc(s []byte, f func(r rune) bool) int
func strings.IndexRune(s string, r rune) intfunc bytes.IndexRune(s []byte, r rune) int
func strings.LastIndex(s, sep string) intfunc bytes.LastIndex(s, sep []byte) int
func strings.LastIndexAny(s, chars string) intfunc bytes.LastIndexAny(s []byte, chars string) int
func strings.LastIndexByte(s string, c byte) intfunc bytes.LastIndexByte(s []byte, c byte) int
func strings.LastIndexFunc(s string, f func(rune) bool) intfunc bytes.LastIndexFunc(s []byte, f func(r rune) bool) int
func bytes.NewBufferString(s string) *bytes.Bufferfunc bytes.NewBuffer(buf []byte *bytes.Buffer
func (h *hash/maphash.Hash) WriteString(s string) (int, error)func (h *hash/maphash.Hash) Write(b []byte) (int, error)
func (rw *net/http/httptest.ResponseRecorder) WriteString(str string) (int, error)func (rw *net/http/httptest.ResponseRecorder) Write(buf []byte) (int, error)
func (f *os.File) WriteString(s string) (n int, err error)func (f *os.File) Write(b []byte) (n int, err error)
func regexp.MatchString(pattern string, s string) (bool, error)func regexp.Match(pattern string, b []byte) (bool, error)
func (re *regexp.Regexp) FindAllStringIndex(s string, n int) [][]intfunc (re *regexp.Regexp) FindAllIndex(b []byte, n int) [][]int
func (re *regexp.Regexp) FindAllStringSubmatch(s string, n int) [][]stringfunc (re *regexp.Regexp) FindAllSubmatch(b []byte, n int) [][][]byte
func (re *regexp.Regexp) FindStringIndex(s string) (loc []int)func (re *regexp.Regexp) FindIndex(b []byte) (loc []int)
func (re *regexp.Regexp) FindStringSubmatchIndex(s string) []intfunc (re *regexp.Regexp) FindSubmatchIndex(b []byte) []int
func (re *regexp.Regexp) MatchString(s string) boolfunc (re *regexp.Regexp) Match(b []byte) bool
func (b *strings.Builder) WriteString(s string) error + func (b *strings.Builder) Write(p []byte) (int, error) + func (b *strings.Builder) WriteRune(r rune) (int, error) +
func utf8.ValidString(s string) boolfunc utf8.Valid(p []byte) bool
func utf8.FullRuneInString(s string) boolfunc utf8.FullRune(p []byte) bool
func utf8.RuneCountInString(s string) (n int)func utf8.RuneCount(p []byte) int
func utf8.DecodeLastRuneInString(s string) (rune, int)func utf8.DecodeLastRune(p []byte) (rune, int)
func utf8.DecodeRuneInString(s string) (une, int)func utf8.DecodeRune(p []byte) (rune, int)
diff --git a/vendor/github.com/butuzov/mirror/Makefile b/vendor/github.com/butuzov/mirror/Makefile new file mode 100644 index 00000000..b4b952b0 --- /dev/null +++ b/vendor/github.com/butuzov/mirror/Makefile @@ -0,0 +1,58 @@ +# --- Required ---------------------------------------------------------------- +export PATH := $(PWD)/bin:$(PATH) # ./bin to $PATH +export SHELL := bash # Default Shell + +GOPKGS := $(shell go list ./... | grep -vE "(cmd|sandbox|testdata)" | tr -s '\n' ',' | sed 's/.\{1\}$$//' ) + + +build: + @ go build -trimpath -ldflags="-w -s" \ + -o bin/mirror ./cmd/mirror/ + +build-race: + @ go build -race -trimpath -ldflags="-w -s" \ + -o bin/mirror ./cmd/mirror/ + +tests: + go test -v -count=1 -race \ + -failfast \ + -parallel=2 \ + -timeout=1m \ + -covermode=atomic \ + -coverpkg=$(GOPKGS) -coverprofile=coverage.cov ./... + +tests-summary: + go test -v -count=1 -race \ + -failfast \ + -parallel=2 \ + -timeout=1m \ + -covermode=atomic \ + -coverpkg=$(GOPKGS) -coverprofile=coverage.cov --json ./... | tparse -all + +test-generate: + go run ./cmd/internal/generate-tests/ "$(PWD)/testdata" + +lints: + golangci-lint run --no-config ./... -D deadcode --skip-dirs "^(cmd|sandbox|testdata)" + + +cover: + go tool cover -html=coverage.cov + +install: + go install -trimpath -v -ldflags="-w -s" \ + ./cmd/mirror + +funcs: + echo "" > "out/results.txt" + go list std | grep -v "vendor" | grep -v "internal" | \ + xargs -I {} sh -c 'go doc -all {} > out/$(basename {}).txt' + +bin/goreleaser: + @curl -Ls https://github.com/goreleaser/goreleaser/releases/download/v1.17.2/goreleaser_Darwin_all.tar.gz | tar -zOxf - goreleaser > ./bin/goreleaser + chmod 0755 ./bin/goreleaser + +test-release: bin/goreleaser + goreleaser release --help + goreleaser release -f .goreleaser.yaml \ + --skip-validate --skip-publish --clean diff --git a/vendor/github.com/butuzov/mirror/Taskfile.yml b/vendor/github.com/butuzov/mirror/Taskfile.yml new file mode 100644 index 00000000..26c9ba25 --- /dev/null +++ b/vendor/github.com/butuzov/mirror/Taskfile.yml @@ -0,0 +1,28 @@ +version: '3' + +tasks: + default: + sources: + - "./**/*.go" + method: timestamp + cmds: + - clear + - make build + - make build-race + - task: lints + # - make test-generate + - task: tests + - cmd: go run ./cmd/mirror/ --with-tests --with-debug ./sandbox + ignore_error: true + + testcase: go test -v -failfast -count=1 -run "TestAll/{{ .Case }}" ./... + + tests: + cmds: + - cmd: make tests + ignore_error: true + + lints: + cmds: + - cmd: make lints + ignore_error: true diff --git a/vendor/github.com/butuzov/mirror/analyzer.go b/vendor/github.com/butuzov/mirror/analyzer.go new file mode 100644 index 00000000..13ded46c --- /dev/null +++ b/vendor/github.com/butuzov/mirror/analyzer.go @@ -0,0 +1,144 @@ +package mirror + +import ( + "flag" + "go/ast" + "strings" + + "github.com/butuzov/mirror/internal/checker" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/inspector" +) + +func NewAnalyzer() *analysis.Analyzer { + flags := flags() + + return &analysis.Analyzer{ + Name: "mirror", + Doc: "reports wrong mirror patterns of bytes/strings usage", + Run: run, + Requires: []*analysis.Analyzer{ + inspect.Analyzer, + }, + Flags: flags, + } +} + +func run(pass *analysis.Pass) (interface{}, error) { + withTests := pass.Analyzer.Flags.Lookup("with-tests").Value.String() == "true" + // --- Reporting violations via issues --------------------------------------- + for _, violation := range Run(pass, withTests) { + pass.Report(violation.Diagnostic(pass.Fset)) + } + + return nil, nil +} + +func Run(pass *analysis.Pass, withTests bool) []*checker.Violation { + violations := []*checker.Violation{} + // --- Setup ----------------------------------------------------------------- + + check := checker.New( + BytesFunctions, BytesBufferMethods, + RegexpFunctions, RegexpRegexpMethods, + StringFunctions, StringsBuilderMethods, + BufioMethods, HTTPTestMethods, + OsFileMethods, MaphashMethods, + UTF8Functions, + ) + + check.Type = checker.WrapType(pass.TypesInfo) + check.Print = checker.WrapPrint(pass.Fset) + + ins, _ := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) + imports := checker.Load(pass.Fset, ins) + + // --- Preorder Checker ------------------------------------------------------ + ins.Preorder([]ast.Node{(*ast.CallExpr)(nil)}, func(n ast.Node) { + callExpr := n.(*ast.CallExpr) + fileName := pass.Fset.Position(callExpr.Pos()).Filename + + if !withTests && strings.HasSuffix(fileName, "_test.go") { + return + } + + // ------------------------------------------------------------------------- + switch expr := callExpr.Fun.(type) { + // NOTE(butuzov): Regular calls (`*ast.SelectorExpr`) like strings.HasPrefix + // or re.Match are handled by this check + case *ast.SelectorExpr: + + x, ok := expr.X.(*ast.Ident) + if !ok { + return + } + + // TODO(butuzov): Add check for the ast.ParenExpr in e.Fun so we can + // target the constructions like this (and other calls) + // ----------------------------------------------------------------------- + // Example: + // (&maphash.Hash{}).Write([]byte("foobar")) + // ----------------------------------------------------------------------- + + // Case 1: Is this is a function call? + pkgName, name := x.Name, expr.Sel.Name + if pkg, ok := imports.Lookup(fileName, pkgName); ok { + if v := check.Match(pkg, name); v != nil { + if args, found := check.Handle(v, callExpr); found { + violations = append(violations, v.With(check.Print(expr.X), callExpr, args)) + } + return + } + } + + // Case 2: Is this is a method call? + tv := pass.TypesInfo.Types[expr.X] + if !tv.IsValue() || tv.Type == nil { + return + } + + pkgStruct, name := cleanAsterisk(tv.Type.String()), expr.Sel.Name + for _, v := range check.Matches(pkgStruct, name) { + if v == nil { + continue + } + + if args, found := check.Handle(v, callExpr); found { + violations = append(violations, v.With(check.Print(expr.X), callExpr, args)) + return + } + } + + case *ast.Ident: + // NOTE(butuzov): Special case of "." imported packages, only functions. + + if pkg, ok := imports.Lookup(fileName, "."); ok { + if v := check.Match(pkg, expr.Name); v != nil { + if args, found := check.Handle(v, callExpr); found { + violations = append(violations, v.With(nil, callExpr, args)) + } + return + } + } + } + }) + + return violations +} + +func flags() flag.FlagSet { + set := flag.NewFlagSet("", flag.PanicOnError) + set.Bool("with-tests", false, "do not skip tests in reports") + set.Bool("with-debug", false, "debug linter run (development only)") + return *set +} + +func cleanAsterisk(s string) string { + if strings.HasPrefix(s, "*") { + return s[1:] + } + + return s +} diff --git a/vendor/github.com/butuzov/mirror/checkers_bufio.go b/vendor/github.com/butuzov/mirror/checkers_bufio.go new file mode 100644 index 00000000..292ed269 --- /dev/null +++ b/vendor/github.com/butuzov/mirror/checkers_bufio.go @@ -0,0 +1,56 @@ +package mirror + +import "github.com/butuzov/mirror/internal/checker" + +var BufioMethods = []checker.Violation{ + { // (*bufio.Writer).Write + Targets: checker.Bytes, + Type: checker.Method, + Package: "bufio", + Struct: "Writer", + Caller: "Write", + Args: []int{0}, + AltCaller: "WriteString", + + Generate: &checker.Generate{ + PreCondition: `b := bufio.Writer{}`, + Pattern: `Write($0)`, + Returns: 2, + }, + }, + { // (*bufio.Writer).WriteString + Type: checker.Method, + Targets: checker.Strings, + Package: "bufio", + Struct: "Writer", + Caller: "WriteString", + Args: []int{0}, + AltCaller: "Write", + + Generate: &checker.Generate{ + PreCondition: `b := bufio.Writer{}`, + Pattern: `WriteString($0)`, + Returns: 2, + }, + }, + { // (*bufio.Writer).WriteString -> (*bufio.Writer).WriteRune + Targets: checker.Strings, + Type: checker.Method, + Package: "bufio", + Struct: "Writer", + Caller: "WriteString", + Args: []int{0}, + ArgsType: checker.Rune, + AltCaller: "WriteRune", + }, + // { // (*bufio.Writer).WriteString -> (*bufio.Writer).WriteByte + // Targets: checker.Strings, + // Type: checker.Method, + // Package: "strings", + // Struct: "Builder", + // Caller: "WriteString", + // Args: []int{0}, + // ArgsType: checker.Byte, + // AltCaller: "WriteByte", // byte + // }, +} diff --git a/vendor/github.com/butuzov/mirror/checkers_bytes.go b/vendor/github.com/butuzov/mirror/checkers_bytes.go new file mode 100644 index 00000000..c490a378 --- /dev/null +++ b/vendor/github.com/butuzov/mirror/checkers_bytes.go @@ -0,0 +1,326 @@ +package mirror + +import "github.com/butuzov/mirror/internal/checker" + +var ( + BytesFunctions = []checker.Violation{ + { // bytes.NewBuffer + Targets: checker.Bytes, + Type: checker.Function, + Package: "bytes", + Caller: "NewBuffer", + Args: []int{0}, + AltCaller: "NewBufferString", + + Generate: &checker.Generate{ + Pattern: `NewBuffer($0)`, + Returns: 1, + }, + }, + { // bytes.NewBufferString + Targets: checker.Strings, + Type: checker.Function, + Package: "bytes", + Caller: "NewBufferString", + Args: []int{0}, + AltCaller: "NewBuffer", + + Generate: &checker.Generate{ + Pattern: `NewBufferString($0)`, + Returns: 1, + }, + }, + { // bytes.Compare: + Targets: checker.Bytes, + Type: checker.Function, + Package: "bytes", + Caller: "Compare", + Args: []int{0, 1}, + AltPackage: "strings", + AltCaller: "Compare", + + Generate: &checker.Generate{ + Pattern: `Compare($0, $1)`, + Returns: 1, + }, + }, + { // bytes.Contains: + Targets: checker.Bytes, + Type: checker.Function, + Package: "bytes", + Caller: "Contains", + Args: []int{0, 1}, + AltPackage: "strings", + AltCaller: "Contains", + + Generate: &checker.Generate{ + Pattern: `Contains($0, $1)`, + Returns: 1, + }, + }, + { // bytes.ContainsAny + Targets: checker.Bytes, + Type: checker.Function, + Package: "bytes", + Caller: "ContainsAny", + Args: []int{0}, + AltPackage: "strings", + AltCaller: "ContainsAny", + + Generate: &checker.Generate{ + Pattern: `ContainsAny($0, "f")`, + Returns: 1, + }, + }, + { // bytes.ContainsRune + Targets: checker.Bytes, + Type: checker.Function, + Package: "bytes", + Caller: "ContainsRune", + Args: []int{0}, + AltPackage: "strings", + AltCaller: "ContainsRune", + + Generate: &checker.Generate{ + Pattern: `ContainsRune($0, 'ф')`, + Returns: 1, + }, + }, + { // bytes.Count + Targets: checker.Bytes, + Type: checker.Function, + Package: "bytes", + Caller: "Count", + Args: []int{0, 1}, + AltPackage: "strings", + AltCaller: "Count", + + Generate: &checker.Generate{ + Pattern: `Count($0, $1)`, + Returns: 1, + }, + }, + { // bytes.EqualFold + Targets: checker.Bytes, + Type: checker.Function, + Package: "bytes", + Caller: "EqualFold", + Args: []int{0, 1}, + AltPackage: "strings", + AltCaller: "EqualFold", + + Generate: &checker.Generate{ + Pattern: `EqualFold($0, $1)`, + Returns: 1, + }, + }, + + { // bytes.HasPrefix + Targets: checker.Bytes, + Type: checker.Function, + Package: "bytes", + Caller: "HasPrefix", + Args: []int{0, 1}, + AltPackage: "strings", + AltCaller: "HasPrefix", + + Generate: &checker.Generate{ + Pattern: `HasPrefix($0, $1)`, + Returns: 1, + }, + }, + { // bytes.HasSuffix + Targets: checker.Bytes, + Type: checker.Function, + Package: "bytes", + Caller: "HasSuffix", + Args: []int{0, 1}, + AltPackage: "strings", + AltCaller: "HasSuffix", + + Generate: &checker.Generate{ + Pattern: `HasSuffix($0, $1)`, + Returns: 1, + }, + }, + { // bytes.Index + Targets: checker.Bytes, + Type: checker.Function, + Package: "bytes", + Caller: "Index", + Args: []int{0, 1}, + AltPackage: "strings", + AltCaller: "Index", + + Generate: &checker.Generate{ + Pattern: `Index($0, $1)`, + Returns: 1, + }, + }, + { // bytes.IndexAny + Targets: checker.Bytes, + Type: checker.Function, + Package: "bytes", + Caller: "IndexAny", + Args: []int{0}, + AltPackage: "strings", + AltCaller: "IndexAny", + + Generate: &checker.Generate{ + Pattern: `IndexAny($0, "f")`, + Returns: 1, + }, + }, + { // bytes.IndexByte + Targets: checker.Bytes, + Type: checker.Function, + Package: "bytes", + Caller: "IndexByte", + Args: []int{0}, + AltPackage: "strings", + AltCaller: "IndexByte", + + Generate: &checker.Generate{ + Pattern: `IndexByte($0, 'f')`, + Returns: 1, + }, + }, + { // bytes.IndexFunc + Targets: checker.Bytes, + Type: checker.Function, + Package: "bytes", + Caller: "IndexFunc", + Args: []int{0}, + AltPackage: "strings", + AltCaller: "IndexFunc", + + Generate: &checker.Generate{ + Pattern: `IndexFunc($0, func(rune) bool {return true })`, + Returns: 1, + }, + }, + { // bytes.IndexRune + Targets: checker.Bytes, + Type: checker.Function, + Package: "bytes", + Caller: "IndexRune", + Args: []int{0}, + AltPackage: "strings", + AltCaller: "IndexRune", + + Generate: &checker.Generate{ + Pattern: `IndexRune($0, rune('ф'))`, + Returns: 1, + }, + }, + { // bytes.LastIndex + Targets: checker.Bytes, + Type: checker.Function, + Package: "bytes", + Caller: "LastIndex", + Args: []int{0, 1}, + AltPackage: "strings", + AltCaller: "LastIndex", + + Generate: &checker.Generate{ + Pattern: `LastIndex($0, $1)`, + Returns: 1, + }, + }, + { // bytes.LastIndexAny + Targets: checker.Bytes, + Type: checker.Function, + Package: "bytes", + Caller: "LastIndexAny", + Args: []int{0}, + AltPackage: "strings", + AltCaller: "LastIndexAny", + + Generate: &checker.Generate{ + Pattern: `LastIndexAny($0, "ф")`, + Returns: 1, + }, + }, + { // bytes.LastIndexByte + Targets: checker.Bytes, + Type: checker.Function, + Package: "bytes", + Caller: "LastIndexByte", + Args: []int{0}, + AltPackage: "strings", + AltCaller: "LastIndexByte", + + Generate: &checker.Generate{ + Pattern: `LastIndexByte($0, 'f')`, + Returns: 1, + }, + }, + { // bytes.LastIndexFunc + Targets: checker.Bytes, + Type: checker.Function, + Package: "bytes", + Caller: "LastIndexFunc", + Args: []int{0}, + AltPackage: "strings", + AltCaller: "LastIndexFunc", + + Generate: &checker.Generate{ + Pattern: `LastIndexFunc($0, func(rune) bool {return true })`, + Returns: 1, + }, + }, + } + + BytesBufferMethods = []checker.Violation{ + { // (*bytes.Buffer).Write + Targets: checker.Bytes, + Type: checker.Method, + Package: "bytes", + Struct: "Buffer", + Caller: "Write", + Args: []int{0}, + AltCaller: "WriteString", + + Generate: &checker.Generate{ + PreCondition: `bb := bytes.Buffer{}`, + Pattern: `Write($0)`, + Returns: 2, + }, + }, + { // (*bytes.Buffer).WriteString + Targets: checker.Strings, + Type: checker.Method, + Package: "bytes", + Struct: "Buffer", + Caller: "WriteString", + Args: []int{0}, + AltCaller: "Write", + + Generate: &checker.Generate{ + PreCondition: `bb := bytes.Buffer{}`, + Pattern: `WriteString($0)`, + Returns: 2, + }, + }, + { // (*bytes.Buffer).WriteString -> (*bytes.Buffer).WriteRune + Targets: checker.Strings, + Type: checker.Method, + Package: "bytes", + Struct: "Buffer", + Caller: "WriteString", + Args: []int{0}, + ArgsType: checker.Rune, + AltCaller: "WriteRune", + }, + // { // (*bytes.Buffer).WriteString -> (*bytes.Buffer).WriteByte + // Targets: checker.Strings, + // Type: checker.Method, + // Package: "bytes", + // Struct: "Buffer", + // Caller: "WriteString", + // Args: []int{0}, + // ArgsType: checker.Byte, + // AltCaller: "WriteByte", + // }, + } +) diff --git a/vendor/github.com/butuzov/mirror/checkers_httptest.go b/vendor/github.com/butuzov/mirror/checkers_httptest.go new file mode 100644 index 00000000..ae675093 --- /dev/null +++ b/vendor/github.com/butuzov/mirror/checkers_httptest.go @@ -0,0 +1,36 @@ +package mirror + +import "github.com/butuzov/mirror/internal/checker" + +var HTTPTestMethods = []checker.Violation{ + { // (*net/http/httptest.ResponseRecorder).Write + Targets: checker.Bytes, + Type: checker.Method, + Package: "net/http/httptest", + Struct: "ResponseRecorder", + Caller: "Write", + Args: []int{0}, + AltCaller: "WriteString", + + Generate: &checker.Generate{ + PreCondition: `h := httptest.ResponseRecorder{}`, + Pattern: `Write($0)`, + Returns: 2, + }, + }, + { // (*net/http/httptest.ResponseRecorder).WriteString + Targets: checker.Strings, + Type: checker.Method, + Package: "net/http/httptest", + Struct: "ResponseRecorder", + Caller: "WriteString", + Args: []int{0}, + AltCaller: "Write", + + Generate: &checker.Generate{ + PreCondition: `h := httptest.ResponseRecorder{}`, + Pattern: `WriteString($0)`, + Returns: 2, + }, + }, +} diff --git a/vendor/github.com/butuzov/mirror/checkers_maphash.go b/vendor/github.com/butuzov/mirror/checkers_maphash.go new file mode 100644 index 00000000..4d184d2a --- /dev/null +++ b/vendor/github.com/butuzov/mirror/checkers_maphash.go @@ -0,0 +1,36 @@ +package mirror + +import "github.com/butuzov/mirror/internal/checker" + +var MaphashMethods = []checker.Violation{ + { // (*hash/maphash).Write + Targets: checker.Bytes, + Type: checker.Method, + Package: "hash/maphash", + Struct: "Hash", + Caller: "Write", + Args: []int{0}, + AltCaller: "WriteString", + + Generate: &checker.Generate{ + PreCondition: `h := maphash.Hash{}`, + Pattern: `Write($0)`, + Returns: 2, + }, + }, + { // (*hash/maphash).WriteString + Targets: checker.Strings, + Type: checker.Method, + Package: "hash/maphash", + Struct: "Hash", + Caller: "WriteString", + Args: []int{0}, + AltCaller: "Write", + + Generate: &checker.Generate{ + PreCondition: `h := maphash.Hash{}`, + Pattern: `WriteString($0)`, + Returns: 2, + }, + }, +} diff --git a/vendor/github.com/butuzov/mirror/checkers_os.go b/vendor/github.com/butuzov/mirror/checkers_os.go new file mode 100644 index 00000000..09f5a18e --- /dev/null +++ b/vendor/github.com/butuzov/mirror/checkers_os.go @@ -0,0 +1,36 @@ +package mirror + +import "github.com/butuzov/mirror/internal/checker" + +var OsFileMethods = []checker.Violation{ + { // (*os.File).Write + Targets: checker.Bytes, + Type: checker.Method, + Package: "os", + Struct: "File", + Caller: "Write", + Args: []int{0}, + AltCaller: "WriteString", + + Generate: &checker.Generate{ + PreCondition: `f := &os.File{}`, + Pattern: `Write($0)`, + Returns: 2, + }, + }, + { // (*os.File).WriteString + Targets: checker.Strings, + Type: checker.Method, + Package: "os", + Struct: "File", + Caller: "WriteString", + Args: []int{0}, + AltCaller: "Write", + + Generate: &checker.Generate{ + PreCondition: `f := &os.File{}`, + Pattern: `WriteString($0)`, + Returns: 2, + }, + }, +} diff --git a/vendor/github.com/butuzov/mirror/checkers_regexp.go b/vendor/github.com/butuzov/mirror/checkers_regexp.go new file mode 100644 index 00000000..17175e02 --- /dev/null +++ b/vendor/github.com/butuzov/mirror/checkers_regexp.go @@ -0,0 +1,187 @@ +package mirror + +import "github.com/butuzov/mirror/internal/checker" + +var ( + RegexpFunctions = []checker.Violation{ + { // regexp.Match + Targets: checker.Bytes, + Type: checker.Function, + Package: "regexp", + Caller: "Match", + Args: []int{1}, + AltCaller: "MatchString", + + Generate: &checker.Generate{ + Pattern: `Match("foo", $0)`, + Returns: 2, + }, + }, + { // regexp.MatchString + Targets: checker.Strings, + Type: checker.Function, + Package: "regexp", + Caller: "MatchString", + Args: []int{1}, + AltCaller: "Match", + + Generate: &checker.Generate{ + Pattern: `MatchString("foo", $0)`, + Returns: 2, + }, + }, + } + + RegexpRegexpMethods = []checker.Violation{ + { // (*regexp.Regexp).Match + Targets: checker.Bytes, + Type: checker.Method, + Package: "regexp", + Struct: "Regexp", + Caller: "Match", + Args: []int{0}, + AltCaller: "MatchString", + + Generate: &checker.Generate{ + PreCondition: `re := regexp.MustCompile(".*")`, + Pattern: `Match($0)`, + Returns: 1, + }, + }, + { // (*regexp.Regexp).MatchString + Targets: checker.Strings, + Type: checker.Method, + Package: "regexp", + Struct: "Regexp", + Caller: "MatchString", + Args: []int{0}, + AltCaller: "Match", + + Generate: &checker.Generate{ + PreCondition: `re := regexp.MustCompile(".*")`, + Pattern: `MatchString($0)`, + Returns: 1, + }, + }, + { // (*regexp.Regexp).FindAllIndex + Targets: checker.Bytes, + Type: checker.Method, + Package: "regexp", + Struct: "Regexp", + Caller: "FindAllIndex", + Args: []int{0}, + AltCaller: "FindAllStringIndex", + + Generate: &checker.Generate{ + PreCondition: `re := regexp.MustCompile(".*")`, + Pattern: `FindAllIndex($0, 1)`, + Returns: 1, + }, + }, + { // (*regexp.Regexp).FindAllStringIndex + Targets: checker.Strings, + Type: checker.Method, + Package: "regexp", + Struct: "Regexp", + Caller: "FindAllStringIndex", + Args: []int{0}, + AltCaller: "FindAllIndex", + + Generate: &checker.Generate{ + PreCondition: `re := regexp.MustCompile(".*")`, + Pattern: `FindAllStringIndex($0, 1)`, + Returns: 1, + }, + }, + { // (*regexp.Regexp).FindAllSubmatchIndex + Targets: checker.Bytes, + Type: checker.Method, + Package: "regexp", + Struct: "Regexp", + Caller: "FindAllSubmatchIndex", + Args: []int{0}, + AltCaller: "FindAllStringSubmatchIndex", + + Generate: &checker.Generate{ + PreCondition: `re := regexp.MustCompile(".*")`, + Pattern: `FindAllSubmatchIndex($0, 1)`, + Returns: 1, + }, + }, + { // (*regexp.Regexp).FindAllStringSubmatchIndex + Targets: checker.Strings, + Type: checker.Method, + Package: "regexp", + Struct: "Regexp", + Caller: "FindAllStringSubmatchIndex", + Args: []int{0}, + AltCaller: "FindAllSubmatchIndex", + + Generate: &checker.Generate{ + PreCondition: `re := regexp.MustCompile(".*")`, + Pattern: `FindAllStringSubmatchIndex($0, 1)`, + Returns: 1, + }, + }, + { // (*regexp.Regexp).FindIndex + Targets: checker.Bytes, + Type: checker.Method, + Package: "regexp", + Struct: "Regexp", + Caller: "FindIndex", + Args: []int{0}, + AltCaller: "FindStringIndex", + + Generate: &checker.Generate{ + PreCondition: `re := regexp.MustCompile(".*")`, + Pattern: `FindIndex($0)`, + Returns: 1, + }, + }, + { // (*regexp.Regexp).FindStringIndex + Targets: checker.Strings, + Type: checker.Method, + Package: "regexp", + Struct: "Regexp", + Caller: "FindStringIndex", + Args: []int{0}, + AltCaller: "FindIndex", + + Generate: &checker.Generate{ + PreCondition: `re := regexp.MustCompile(".*")`, + Pattern: `FindStringIndex($0)`, + Returns: 1, + }, + }, + { // (*regexp.Regexp).FindSubmatchIndex + Targets: checker.Bytes, + Type: checker.Method, + Package: "regexp", + Struct: "Regexp", + Caller: "FindSubmatchIndex", + Args: []int{0}, + AltCaller: "FindStringSubmatchIndex", + + Generate: &checker.Generate{ + PreCondition: `re := regexp.MustCompile(".*")`, + Pattern: `FindSubmatchIndex($0)`, + Returns: 1, + }, + }, + { // (*regexp.Regexp).FindStringSubmatchIndex + Targets: checker.Strings, + Type: checker.Method, + Package: "regexp", + Struct: "Regexp", + Caller: "FindStringSubmatchIndex", + Args: []int{0}, + AltCaller: "FindSubmatchIndex", + + Generate: &checker.Generate{ + PreCondition: `re := regexp.MustCompile(".*")`, + Pattern: `FindStringSubmatchIndex($0)`, + Returns: 1, + }, + }, + } +) diff --git a/vendor/github.com/butuzov/mirror/checkers_strings.go b/vendor/github.com/butuzov/mirror/checkers_strings.go new file mode 100644 index 00000000..ead7e9cc --- /dev/null +++ b/vendor/github.com/butuzov/mirror/checkers_strings.go @@ -0,0 +1,299 @@ +package mirror + +import "github.com/butuzov/mirror/internal/checker" + +var ( + StringFunctions = []checker.Violation{ + { // strings.Compare + Targets: checker.Strings, + Type: checker.Function, + Package: "strings", + Caller: "Compare", + Args: []int{0, 1}, + AltPackage: "bytes", + AltCaller: "Compare", + + Generate: &checker.Generate{ + Pattern: `Compare($0,$1)`, + Returns: 1, + }, + }, + { // strings.Contains + Targets: checker.Strings, + Type: checker.Function, + Package: "strings", + Caller: "Contains", + Args: []int{0, 1}, + AltPackage: "bytes", + AltCaller: "Contains", + + Generate: &checker.Generate{ + Pattern: `Contains($0,$1)`, + Returns: 1, + }, + }, + { // strings.ContainsAny + Targets: checker.Strings, + Type: checker.Function, + Package: "strings", + Caller: "ContainsAny", + Args: []int{0}, + AltPackage: "bytes", + AltCaller: "ContainsAny", + + Generate: &checker.Generate{ + Pattern: `ContainsAny($0,"foobar")`, + Returns: 1, + }, + }, + { // strings.ContainsRune + Targets: checker.Strings, + Type: checker.Function, + Package: "strings", + Caller: "ContainsRune", + Args: []int{0}, + AltPackage: "bytes", + AltCaller: "ContainsRune", + + Generate: &checker.Generate{ + Pattern: `ContainsRune($0,'ф')`, + Returns: 1, + }, + }, + { // strings.Count + Targets: checker.Strings, + Type: checker.Function, + Package: "strings", + Caller: "Count", + Args: []int{0, 1}, + AltPackage: "bytes", + AltCaller: "Count", + + Generate: &checker.Generate{ + Pattern: `Count($0, $1)`, + Returns: 1, + }, + }, + { // strings.EqualFold + Targets: checker.Strings, + Type: checker.Function, + Package: "strings", + Caller: "EqualFold", + Args: []int{0, 1}, + AltPackage: "bytes", + AltCaller: "EqualFold", + + Generate: &checker.Generate{ + Pattern: `EqualFold($0,$1)`, + Returns: 1, + }, + }, + { // strings.HasPrefix + Targets: checker.Strings, + Type: checker.Function, + Package: "strings", + Caller: "HasPrefix", + Args: []int{0, 1}, + AltPackage: "bytes", + AltCaller: "HasPrefix", + + Generate: &checker.Generate{ + Pattern: `HasPrefix($0,$1)`, + Returns: 1, + }, + }, + { // strings.HasSuffix + Targets: checker.Strings, + Type: checker.Function, + Package: "strings", + Caller: "HasSuffix", + Args: []int{0, 1}, + AltPackage: "bytes", + AltCaller: "HasSuffix", + + Generate: &checker.Generate{ + Pattern: `HasSuffix($0,$1)`, + Returns: 1, + }, + }, + { // strings.Index + Targets: checker.Strings, + Type: checker.Function, + Package: "strings", + Caller: "Index", + Args: []int{0, 1}, + AltPackage: "bytes", + AltCaller: "Index", + + Generate: &checker.Generate{ + Pattern: `Index($0,$1)`, + Returns: 1, + }, + }, + { // strings.IndexAny + Targets: checker.Strings, + Type: checker.Function, + Package: "strings", + Caller: "IndexAny", + Args: []int{0}, + AltPackage: "bytes", + AltCaller: "IndexAny", + + Generate: &checker.Generate{ + Pattern: `IndexAny($0, "f")`, + Returns: 1, + }, + }, + { // strings.IndexByte + Targets: checker.Strings, + Type: checker.Function, + Package: "strings", + Caller: "IndexByte", + Args: []int{0}, + AltPackage: "bytes", + AltCaller: "IndexByte", + + Generate: &checker.Generate{ + Pattern: `IndexByte($0, byte('f'))`, + Returns: 1, + }, + }, + { // strings.IndexFunc + Targets: checker.Strings, + Type: checker.Function, + Package: "strings", + Caller: "IndexFunc", + Args: []int{0}, + AltPackage: "bytes", + AltCaller: "IndexFunc", + + Generate: &checker.Generate{ + Pattern: `IndexFunc($0,func(r rune) bool { return true })`, + Returns: 1, + }, + }, + { // strings.IndexRune + Targets: checker.Strings, + Type: checker.Function, + Package: "strings", + Caller: "IndexRune", + Args: []int{0}, + AltPackage: "bytes", + AltCaller: "IndexRune", + + Generate: &checker.Generate{ + Pattern: `IndexRune($0, rune('ф'))`, + Returns: 1, + }, + }, + { // strings.LastIndex + Targets: checker.Strings, + Type: checker.Function, + Package: "strings", + Caller: "LastIndex", + Args: []int{0, 1}, + AltPackage: "bytes", + AltCaller: "LastIndex", + + Generate: &checker.Generate{ + Pattern: `LastIndex($0,$1)`, + Returns: 1, + }, + }, + { // strings.LastIndexAny + Targets: checker.Strings, + Type: checker.Function, + Package: "strings", + Caller: "LastIndexAny", + Args: []int{0}, + AltPackage: "bytes", + AltCaller: "LastIndexAny", + + Generate: &checker.Generate{ + Pattern: `LastIndexAny($0,"f")`, + Returns: 1, + }, + }, + { // strings.LastIndexByte + Targets: checker.Strings, + Type: checker.Function, + Package: "strings", + Caller: "LastIndexByte", + Args: []int{0}, + AltPackage: "bytes", + AltCaller: "LastIndexByte", + + Generate: &checker.Generate{ + Pattern: `LastIndexByte($0, byte('f'))`, + Returns: 1, + }, + }, + { // strings.LastIndexFunc + Targets: checker.Strings, + Type: checker.Function, + Package: "strings", + Caller: "LastIndexFunc", + Args: []int{0}, + AltPackage: "bytes", + AltCaller: "LastIndexFunc", + + Generate: &checker.Generate{ + Pattern: `LastIndexFunc($0, func(r rune) bool { return true })`, + Returns: 1, + }, + }, + } + + StringsBuilderMethods = []checker.Violation{ + { // (*strings.Builder).Write + Targets: checker.Bytes, + Type: checker.Method, + Package: "strings", + Struct: "Builder", + Caller: "Write", + Args: []int{0}, + AltCaller: "WriteString", + + Generate: &checker.Generate{ + PreCondition: `builder := strings.Builder{}`, + Pattern: `Write($0)`, + Returns: 2, + }, + }, + { // (*strings.Builder).WriteString + Targets: checker.Strings, + Type: checker.Method, + Package: "strings", + Struct: "Builder", + Caller: "WriteString", + Args: []int{0}, + AltCaller: "Write", + + Generate: &checker.Generate{ + PreCondition: `builder := strings.Builder{}`, + Pattern: `WriteString($0)`, + Returns: 2, + }, + }, + { // (*strings.Builder).WriteString -> (*strings.Builder).WriteRune + Targets: checker.Strings, + Type: checker.Method, + Package: "strings", + Struct: "Builder", + Caller: "WriteString", + Args: []int{0}, + ArgsType: checker.Rune, + AltCaller: "WriteRune", + }, + // { // (*strings.Builder).WriteString -> (*strings.Builder).WriteByte + // Targets: checker.Strings, + // Type: checker.Method, + // Package: "strings", + // Struct: "Builder", + // Caller: "WriteString", + // Args: []int{0}, + // ArgsType: checker.Byte, + // AltCaller: "WriteByte", // byte + // }, + } +) diff --git a/vendor/github.com/butuzov/mirror/checkers_utf8.go b/vendor/github.com/butuzov/mirror/checkers_utf8.go new file mode 100644 index 00000000..e7c4d5ba --- /dev/null +++ b/vendor/github.com/butuzov/mirror/checkers_utf8.go @@ -0,0 +1,138 @@ +package mirror + +import "github.com/butuzov/mirror/internal/checker" + +var UTF8Functions = []checker.Violation{ + { // utf8.Valid + Type: checker.Function, + Targets: checker.Bytes, + Package: "unicode/utf8", + Caller: "Valid", + Args: []int{0}, + AltCaller: "ValidString", + + Generate: &checker.Generate{ + Pattern: `Valid($0)`, + Returns: 1, + }, + }, + { // utf8.ValidString + Targets: checker.Strings, + Type: checker.Function, + Package: "unicode/utf8", + Caller: "ValidString", + Args: []int{0}, + AltCaller: "Valid", + + Generate: &checker.Generate{ + Pattern: `ValidString($0)`, + Returns: 1, + }, + }, + { // utf8.FullRune + Targets: checker.Bytes, + Type: checker.Function, + Package: "unicode/utf8", + Caller: "FullRune", + Args: []int{0}, + AltCaller: "FullRuneInString", + + Generate: &checker.Generate{ + Pattern: `FullRune($0)`, + Returns: 1, + }, + }, + { // utf8.FullRuneInString + Targets: checker.Strings, + Type: checker.Function, + Package: "unicode/utf8", + Caller: "FullRuneInString", + Args: []int{0}, + AltCaller: "FullRune", + + Generate: &checker.Generate{ + Pattern: `FullRuneInString($0)`, + Returns: 1, + }, + }, + + { // bytes.RuneCount + Targets: checker.Bytes, + Type: checker.Function, + Package: "unicode/utf8", + Caller: "RuneCount", + Args: []int{0}, + AltCaller: "RuneCountInString", + + Generate: &checker.Generate{ + Pattern: `RuneCount($0)`, + Returns: 1, + }, + }, + { // bytes.RuneCountInString + Targets: checker.Strings, + Type: checker.Function, + Package: "unicode/utf8", + Caller: "RuneCountInString", + Args: []int{0}, + AltCaller: "RuneCount", + + Generate: &checker.Generate{ + Pattern: `RuneCountInString($0)`, + Returns: 1, + }, + }, + + { // bytes.DecodeLastRune + Targets: checker.Bytes, + Type: checker.Function, + Package: "unicode/utf8", + Caller: "DecodeLastRune", + Args: []int{0}, + AltCaller: "DecodeLastRuneInString", + + Generate: &checker.Generate{ + Pattern: `DecodeLastRune($0)`, + Returns: 2, + }, + }, + { // utf8.DecodeLastRuneInString + Targets: checker.Strings, + Type: checker.Function, + Package: "unicode/utf8", + Caller: "DecodeLastRuneInString", + Args: []int{0}, + AltCaller: "DecodeLastRune", + + Generate: &checker.Generate{ + Pattern: `DecodeLastRuneInString($0)`, + Returns: 2, + }, + }, + { // utf8.DecodeRune + Targets: checker.Bytes, + Type: checker.Function, + Package: "unicode/utf8", + Caller: "DecodeRune", + Args: []int{0}, + AltCaller: "DecodeRuneInString", + + Generate: &checker.Generate{ + Pattern: `DecodeRune($0)`, + Returns: 2, + }, + }, + { // utf8.DecodeRuneInString + Targets: checker.Strings, + Type: checker.Function, + Package: "unicode/utf8", + Args: []int{0}, + Caller: "DecodeRuneInString", + AltCaller: "DecodeRune", + + Generate: &checker.Generate{ + Pattern: `DecodeRuneInString($0)`, + Returns: 2, + }, + }, +} diff --git a/vendor/github.com/butuzov/mirror/internal/checker/checker.go b/vendor/github.com/butuzov/mirror/internal/checker/checker.go new file mode 100644 index 00000000..c1a94163 --- /dev/null +++ b/vendor/github.com/butuzov/mirror/internal/checker/checker.go @@ -0,0 +1,147 @@ +package checker + +import ( + "bytes" + "go/ast" + "go/printer" + "go/token" + "go/types" + "strings" +) + +// Checker will perform standart check on package and its methods. +type Checker struct { + Violations []Violation // List of available violations + Packages map[string][]int // Storing indexes of Violations per pkg/kg.Struct + Type func(ast.Expr) string // Type Checker closure. + Print func(ast.Node) []byte // String representation of the expresion. +} + +func New(violations ...[]Violation) Checker { + c := Checker{ + Packages: make(map[string][]int), + } + + for i := range violations { + c.register(violations[i]) + } + + return c +} + +// Match will check the available violations we got from checks against +// the `name` caller from package `pkgName`. +func (c *Checker) Match(pkgName, name string) *Violation { + for _, v := range c.Matches(pkgName, name) { + return v + } + + return nil +} + +// Matches do same thing as Match but return a slice of violations +// as only things that require this are bytes.Buffer and strings.Builder +// it only be used in matching methods in analyzer. +func (c *Checker) Matches(pkgName, name string) []*Violation { + var matches []*Violation + checkStruct := strings.Contains(pkgName, ".") + + for _, idx := range c.Packages[pkgName] { + if c.Violations[idx].Caller == name { + if checkStruct == (len(c.Violations[idx].Struct) == 0) { + continue + } + + // copy violation + v := c.Violations[idx] + matches = append(matches, &v) + } + } + + return matches +} + +func (c *Checker) Handle(v *Violation, ce *ast.CallExpr) (map[int]ast.Expr, bool) { + m := map[int]ast.Expr{} + + // We going to check each of elements we mark for checking, in order to find, + // a call that violates our rules. + for _, i := range v.Args { + if i >= len(ce.Args) { + continue + } + + call, ok := ce.Args[i].(*ast.CallExpr) + if !ok { + continue + } + + // is it convertsion call + if !c.callConverts(call) { + continue + } + + // somehow no argument of call + if len(call.Args) == 0 { + continue + } + + // wrong argument type + if normalType(c.Type(call.Args[0])) != v.getArgType() { + continue + } + + m[i] = call.Args[0] + } + + return m, len(m) == len(v.Args) +} + +func (c *Checker) callConverts(ce *ast.CallExpr) bool { + switch ce.Fun.(type) { + case *ast.ArrayType, *ast.Ident: + res := c.Type(ce.Fun) + return res == "[]byte" || res == "string" + } + + return false +} + +// register violations. +func (c *Checker) register(violations []Violation) { + for _, v := range violations { // nolint: gocritic + c.Violations = append(c.Violations, v) + if len(v.Struct) > 0 { + c.registerIdxPer(v.Package + "." + v.Struct) + } + c.registerIdxPer(v.Package) + } +} + +// registerIdxPer will register last added violation element +// under pkg string. +func (c *Checker) registerIdxPer(pkg string) { + c.Packages[pkg] = append(c.Packages[pkg], len(c.Violations)-1) +} + +func WrapType(info *types.Info) func(node ast.Expr) string { + return func(node ast.Expr) string { + if t := info.TypeOf(node); t != nil { + return t.String() + } + + if tv, ok := info.Types[node]; ok { + return tv.Type.Underlying().String() + } + + return "" + } +} + +func WrapPrint(fSet *token.FileSet) func(ast.Node) []byte { + return func(node ast.Node) []byte { + var buf bytes.Buffer + printer.Fprint(&buf, fSet, node) + return buf.Bytes() + } +} diff --git a/vendor/github.com/butuzov/mirror/internal/checker/imports.go b/vendor/github.com/butuzov/mirror/internal/checker/imports.go new file mode 100644 index 00000000..4015de59 --- /dev/null +++ b/vendor/github.com/butuzov/mirror/internal/checker/imports.go @@ -0,0 +1,89 @@ +package checker + +import ( + "go/ast" + "go/token" + "path" + "sort" + "strings" + "sync" + + "golang.org/x/tools/go/ast/inspector" +) + +// Imports represents an imported package in a nice for lookup way... +// +// examples: +// import . "bytes" -> checker.Import{Pkg:"bytes", Val:"."} +// import name "bytes" -> checker.Import{Pkg:"bytes", Val:"name"} +type Import struct { + Pkg string // package name + Name string // alias +} + +type Imports map[string][]Import + +// we are going to have Imports entries to be sorted, but if it has less then +// `sortLowerLimit` elements we are skipping this step as its not going to +// be worth of effort. +const sortLowerLimit int = 13 + +// Package level lock is to prevent import map corruption +var lock sync.RWMutex + +func Load(fs *token.FileSet, ins *inspector.Inspector) Imports { + lock.Lock() + defer lock.Unlock() + + imports := make(Imports) + + // Populate imports map + ins.Preorder([]ast.Node{(*ast.ImportSpec)(nil)}, func(node ast.Node) { + importSpec, _ := node.(*ast.ImportSpec) + + var ( + key = fs.Position(node.Pos()).Filename + pkg = strings.Trim(importSpec.Path.Value, `"`) + name = importSpec.Name.String() + ) + + if importSpec.Name == nil { + name = path.Base(pkg) // note: we need only basename of the package + } + + imports[key] = append(imports[key], Import{ + Pkg: pkg, + Name: name, + }) + }) + + imports.sort() + + return imports +} + +// sort will sort imports for each of the checking files. +func (i *Imports) sort() { + for k := range *i { + if len((*i)[k]) < sortLowerLimit { + continue + } + + k := k + sort.Slice((*i)[k], func(left, right int) bool { + return (*i)[k][left].Name < (*i)[k][right].Name + }) + } +} + +func (i Imports) Lookup(file, pkg string) (string, bool) { + if _, ok := i[file]; ok { + for idx := range i[file] { + if i[file][idx].Name == pkg { + return i[file][idx].Pkg, true + } + } + } + + return "", false +} diff --git a/vendor/github.com/butuzov/mirror/internal/checker/violation.go b/vendor/github.com/butuzov/mirror/internal/checker/violation.go new file mode 100644 index 00000000..375d3c8e --- /dev/null +++ b/vendor/github.com/butuzov/mirror/internal/checker/violation.go @@ -0,0 +1,208 @@ +package checker + +import ( + "bytes" + "fmt" + "go/ast" + "go/printer" + "go/token" + "path" + "strings" + + "golang.org/x/tools/go/analysis" +) + +// Type of violation: can be method or function +type ViolationType int + +const ( + Function ViolationType = iota + 1 + Method +) + +const ( + Strings string = "string" + Bytes string = "[]byte" + Byte string = "byte" + Rune string = "rune" + UntypedRune string = "untyped rune" +) + +// Violation describs what message we going to give to a particular code violation +type Violation struct { + Type ViolationType // + Args []int // Indexes of the arguments needs to be checked + ArgsType string + + Targets string + Package string + AltPackage string + Struct string + Caller string + AltCaller string + + // --- tests generation information + Generate *Generate + + // --- suggestions related info about violation of rules. + base []byte // receiver of the method or pkg name + callExpr *ast.CallExpr // actual call expression, to extract arguments + arguments map[int]ast.Expr // fixed arguments +} + +// Tests (generation) related struct. +type Generate struct { + PreCondition string // Precondition we want to be generated + Pattern string // Generate pattern (for the `want` message) + Returns int // Expected to return n elements +} + +func (v *Violation) With(base []byte, e *ast.CallExpr, args map[int]ast.Expr) *Violation { + v.base = base + v.callExpr = e + v.arguments = args + + return v +} + +func (v *Violation) getArgType() string { + if v.ArgsType != "" { + return v.ArgsType + } + + if v.Targets == Strings { + return Bytes + } + + return Strings +} + +func (v *Violation) Message() string { + if v.Type == Method { + return fmt.Sprintf("avoid allocations with (*%s.%s).%s", + path.Base(v.Package), v.Struct, v.AltCaller) + } + + pkg := v.Package + if len(v.AltPackage) > 0 { + pkg = v.AltPackage + } + + return fmt.Sprintf("avoid allocations with %s.%s", path.Base(pkg), v.AltCaller) +} + +func (v *Violation) suggest(fSet *token.FileSet) []byte { + var buf bytes.Buffer + + if len(v.base) > 0 { + buf.Write(v.base) + buf.WriteString(".") + } + + buf.WriteString(v.AltCaller) + buf.WriteByte('(') + for idx := range v.callExpr.Args { + if arg, ok := v.arguments[idx]; ok { + printer.Fprint(&buf, fSet, arg) + } else { + printer.Fprint(&buf, fSet, v.callExpr.Args[idx]) + } + + if idx != len(v.callExpr.Args)-1 { + buf.WriteString(", ") + } + } + buf.WriteByte(')') + + return buf.Bytes() +} + +func (v *Violation) Diagnostic(fSet *token.FileSet) analysis.Diagnostic { + diagnostic := analysis.Diagnostic{ + Pos: v.callExpr.Pos(), + End: v.callExpr.Pos(), + Message: v.Message(), + } + + var buf bytes.Buffer + printer.Fprint(&buf, fSet, v.callExpr) + noNl := bytes.IndexByte(buf.Bytes(), '\n') < 0 + + // Struct based fix. + if v.Type == Method && noNl { + diagnostic.SuggestedFixes = []analysis.SuggestedFix{{ + Message: "Fix Issue With", + TextEdits: []analysis.TextEdit{{ + Pos: v.callExpr.Pos(), End: v.callExpr.End(), NewText: v.suggest(fSet), + }}, + }} + } + + if v.AltPackage == "" { + v.AltPackage = v.Package + } + + // Hooray! we dont need to change package and redo imports. + if v.Type == Function && v.AltPackage == v.Package && noNl { + diagnostic.SuggestedFixes = []analysis.SuggestedFix{{ + Message: "Fix Issue With", + TextEdits: []analysis.TextEdit{{ + Pos: v.callExpr.Pos(), End: v.callExpr.End(), NewText: v.suggest(fSet), + }}, + }} + } + + // do not change + + return diagnostic +} + +type GolangIssue struct { + Start token.Position + End token.Position + Message string + InlineFix string + Original string +} + +// Issue inteanded to be used only with golangci-lint, bu you can use use it +// alongside Diagnostic if you wish. +func (v *Violation) Issue(fSet *token.FileSet) GolangIssue { + issue := GolangIssue{ + Start: fSet.Position(v.callExpr.Pos()), + End: fSet.Position(v.callExpr.End()), + Message: v.Message(), + } + + // original expression (useful for debug & requied for replace) + var buf bytes.Buffer + printer.Fprint(&buf, fSet, v.callExpr) + issue.Original = buf.String() + + noNl := strings.IndexByte(issue.Original, '\n') < 0 + + if v.Type == Method && noNl { + fix := v.suggest(fSet) + issue.InlineFix = string(fix) + } + + if v.AltPackage == "" { + v.AltPackage = v.Package + } + + // Hooray! we don't need to change package and redo imports. + if v.Type == Function && v.AltPackage == v.Package && noNl { + fix := v.suggest(fSet) + issue.InlineFix = string(fix) + } + + return issue +} + +// ofType normalize input types (mostly typed and untyped runes). +func normalType(s string) string { + if s == UntypedRune { + return Rune + } + return s +} diff --git a/vendor/github.com/butuzov/mirror/readme.md b/vendor/github.com/butuzov/mirror/readme.md new file mode 100644 index 00000000..fcfd1de1 --- /dev/null +++ b/vendor/github.com/butuzov/mirror/readme.md @@ -0,0 +1,60 @@ +# `mirror` [![Code Coverage](https://coveralls.io/repos/github/butuzov/mirror/badge.svg?branch=main)](https://coveralls.io/github/butuzov/mirror?branch=main) [![build status](https://github.com/butuzov/mirror/actions/workflows/main.yaml/badge.svg?branch=main)]() + +`mirror` suggests use of alternative functions/methods in order to gain performance boosts by avoiding unnecessary `[]byte/string` conversion calls. See [MIRROR_FUNCS.md](MIRROR_FUNCS.md) list of mirror functions you can use in go's stdlib. + +## 🇺🇦 PLEASE HELP ME 🇺🇦 +Fundrise for scout drone **DJI Matrice 30T** for my squad (Ukrainian Forces). See more details at [butuzov/README.md](https://github.com/butuzov/butuzov/) + +## Linter Use Cases + +### `github.com/argoproj/argo-cd` + +```go +// Before +func IsValidHostname(hostname string, fqdn bool) bool { + if !fqdn { + return validHostNameRegexp.Match([]byte(hostname)) || validIPv6Regexp.Match([]byte(hostname)) + } else { + return validFQDNRegexp.Match([]byte(hostname)) + } +} + +// After: With alternative method (and lost `else` case) +func IsValidHostname(hostname string, fqdn bool) bool { + if !fqdn { + return validHostNameRegexp.MatchString(hostname) || validIPv6Regexp.MatchString(hostname) + } + + return validFQDNRegexp.MatchString(hostname) +} +``` + +## Install + +``` +go install github.com/butuzov/mirror/cmd/mirror@latest +``` + +## How to use + +You run `mirror` with [`go vet`](https://pkg.go.dev/cmd/vet): + +``` +go vet -vettool=$(which mirror) ./... +# github.com/jcmoraisjr/haproxy-ingress/pkg/common/net/ssl +pkg/common/net/ssl/ssl.go:64:11: avoid allocations with (*os.File).WriteString +pkg/common/net/ssl/ssl.go:161:12: avoid allocations with (*os.File).WriteString +pkg/common/net/ssl/ssl.go:166:3: avoid allocations with (*os.File).WriteString +``` + +Can be called directly: +``` +mirror ./... +# https://github.com/cosmtrek/air +/air/runner/util.go:149:6: avoid allocations with (*regexp.Regexp).MatchString +/air/runner/util.go:173:14: avoid allocations with (*os.File).WriteString +``` + +## Command line + +- You can add checks for `_test.go` files with cli option `--with-tests` diff --git a/vendor/github.com/go-critic/go-critic/checkers/appendAssign_checker.go b/vendor/github.com/go-critic/go-critic/checkers/appendAssign_checker.go index a0727f4a..2a67dcce 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/appendAssign_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/appendAssign_checker.go @@ -16,7 +16,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "appendAssign" - info.Tags = []string{"diagnostic"} + info.Tags = []string{linter.DiagnosticTag} info.Summary = "Detects suspicious append result assignments" info.Before = ` p.positives = append(p.negatives, x) diff --git a/vendor/github.com/go-critic/go-critic/checkers/appendCombine_checker.go b/vendor/github.com/go-critic/go-critic/checkers/appendCombine_checker.go index cce1a8f3..81a7aa30 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/appendCombine_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/appendCombine_checker.go @@ -14,7 +14,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "appendCombine" - info.Tags = []string{"performance"} + info.Tags = []string{linter.PerformanceTag} info.Summary = "Detects `append` chains to the same slice that can be done in a single `append` call" info.Before = ` xs = append(xs, 1) diff --git a/vendor/github.com/go-critic/go-critic/checkers/badCond_checker.go b/vendor/github.com/go-critic/go-critic/checkers/badCond_checker.go index b0834ff8..9be45ccc 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/badCond_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/badCond_checker.go @@ -19,7 +19,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "badCond" - info.Tags = []string{"diagnostic"} + info.Tags = []string{linter.DiagnosticTag} info.Summary = "Detects suspicious condition expressions" info.Before = ` for i := 0; i > n; i++ { diff --git a/vendor/github.com/go-critic/go-critic/checkers/badRegexp_checker.go b/vendor/github.com/go-critic/go-critic/checkers/badRegexp_checker.go index 4b957f0c..6c684505 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/badRegexp_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/badRegexp_checker.go @@ -17,7 +17,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "badRegexp" - info.Tags = []string{"diagnostic", "experimental"} + info.Tags = []string{linter.DiagnosticTag, linter.ExperimentalTag} info.Summary = "Detects suspicious regexp patterns" info.Before = "regexp.MustCompile(`(?:^aa|bb|cc)foo[aba]`)" info.After = "regexp.MustCompile(`^(?:aa|bb|cc)foo[ab]`)" diff --git a/vendor/github.com/go-critic/go-critic/checkers/boolExprSimplify_checker.go b/vendor/github.com/go-critic/go-critic/checkers/boolExprSimplify_checker.go index 9c7897f3..a1c69cb7 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/boolExprSimplify_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/boolExprSimplify_checker.go @@ -20,7 +20,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "boolExprSimplify" - info.Tags = []string{"style", "experimental"} + info.Tags = []string{linter.StyleTag, linter.ExperimentalTag} info.Summary = "Detects bool expressions that can be simplified" info.Before = ` a := !(elapsed >= expectElapsedMin) diff --git a/vendor/github.com/go-critic/go-critic/checkers/builtinShadowDecl_checker.go b/vendor/github.com/go-critic/go-critic/checkers/builtinShadowDecl_checker.go index 0eaf9b85..d8be10ce 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/builtinShadowDecl_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/builtinShadowDecl_checker.go @@ -10,7 +10,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "builtinShadowDecl" - info.Tags = []string{"diagnostic", "experimental"} + info.Tags = []string{linter.DiagnosticTag, linter.ExperimentalTag} info.Summary = "Detects top-level declarations that shadow the predeclared identifiers" info.Before = `type int struct {}` info.After = `type myInt struct {}` diff --git a/vendor/github.com/go-critic/go-critic/checkers/builtinShadow_checker.go b/vendor/github.com/go-critic/go-critic/checkers/builtinShadow_checker.go index b910462a..0b4b7baf 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/builtinShadow_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/builtinShadow_checker.go @@ -10,7 +10,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "builtinShadow" - info.Tags = []string{"style", "opinionated"} + info.Tags = []string{linter.StyleTag, linter.OpinionatedTag} info.Summary = "Detects when predeclared identifiers are shadowed in assignments" info.Before = `len := 10` info.After = `length := 10` diff --git a/vendor/github.com/go-critic/go-critic/checkers/captLocal_checker.go b/vendor/github.com/go-critic/go-critic/checkers/captLocal_checker.go index dd3a4154..b31a6f7f 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/captLocal_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/captLocal_checker.go @@ -10,7 +10,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "captLocal" - info.Tags = []string{"style"} + info.Tags = []string{linter.StyleTag} info.Params = linter.CheckerParams{ "paramsOnly": { Value: true, diff --git a/vendor/github.com/go-critic/go-critic/checkers/caseOrder_checker.go b/vendor/github.com/go-critic/go-critic/checkers/caseOrder_checker.go index e403476f..30675683 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/caseOrder_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/caseOrder_checker.go @@ -11,7 +11,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "caseOrder" - info.Tags = []string{"diagnostic"} + info.Tags = []string{linter.DiagnosticTag} info.Summary = "Detects erroneous case order inside switch statements" info.Before = ` switch x.(type) { diff --git a/vendor/github.com/go-critic/go-critic/checkers/codegenComment_checker.go b/vendor/github.com/go-critic/go-critic/checkers/codegenComment_checker.go index eebe55bc..6eeb0bb5 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/codegenComment_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/codegenComment_checker.go @@ -12,7 +12,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "codegenComment" - info.Tags = []string{"diagnostic"} + info.Tags = []string{linter.DiagnosticTag} info.Summary = "Detects malformed 'code generated' file comments" info.Before = `// This file was automatically generated by foogen` info.After = `// Code generated by foogen. DO NOT EDIT.` diff --git a/vendor/github.com/go-critic/go-critic/checkers/commentFormatting_checker.go b/vendor/github.com/go-critic/go-critic/checkers/commentFormatting_checker.go index 40c8296a..5a9564a0 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/commentFormatting_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/commentFormatting_checker.go @@ -14,7 +14,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "commentFormatting" - info.Tags = []string{"style"} + info.Tags = []string{linter.StyleTag} info.Summary = "Detects comments with non-idiomatic formatting" info.Before = `//This is a comment` info.After = `// This is a comment` diff --git a/vendor/github.com/go-critic/go-critic/checkers/commentedOutCode_checker.go b/vendor/github.com/go-critic/go-critic/checkers/commentedOutCode_checker.go index dcad2966..402ba330 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/commentedOutCode_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/commentedOutCode_checker.go @@ -16,7 +16,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "commentedOutCode" - info.Tags = []string{"diagnostic", "experimental"} + info.Tags = []string{linter.DiagnosticTag, linter.ExperimentalTag} info.Summary = "Detects commented-out code inside function bodies" info.Before = ` // fmt.Println("Debugging hard") diff --git a/vendor/github.com/go-critic/go-critic/checkers/commentedOutImport_checker.go b/vendor/github.com/go-critic/go-critic/checkers/commentedOutImport_checker.go index 2f33973f..e0855da8 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/commentedOutImport_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/commentedOutImport_checker.go @@ -12,7 +12,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "commentedOutImport" - info.Tags = []string{"style", "experimental"} + info.Tags = []string{linter.StyleTag, linter.ExperimentalTag} info.Summary = "Detects commented-out imports" info.Before = ` import ( diff --git a/vendor/github.com/go-critic/go-critic/checkers/defaultCaseOrder_checker.go b/vendor/github.com/go-critic/go-critic/checkers/defaultCaseOrder_checker.go index ef3a4c18..cdebaef9 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/defaultCaseOrder_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/defaultCaseOrder_checker.go @@ -10,7 +10,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "defaultCaseOrder" - info.Tags = []string{"style"} + info.Tags = []string{linter.StyleTag} info.Summary = "Detects when default case in switch isn't on 1st or last position" info.Before = ` switch { diff --git a/vendor/github.com/go-critic/go-critic/checkers/deferInLoop_checker.go b/vendor/github.com/go-critic/go-critic/checkers/deferInLoop_checker.go index 5b9266e2..37c80c86 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/deferInLoop_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/deferInLoop_checker.go @@ -10,7 +10,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "deferInLoop" - info.Tags = []string{"diagnostic", "experimental"} + info.Tags = []string{linter.DiagnosticTag, linter.ExperimentalTag} info.Summary = "Detects loops inside functions that use defer" info.Before = ` for _, filename := range []string{"foo", "bar"} { diff --git a/vendor/github.com/go-critic/go-critic/checkers/deprecatedComment_checker.go b/vendor/github.com/go-critic/go-critic/checkers/deprecatedComment_checker.go index dc4df5df..c61d773d 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/deprecatedComment_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/deprecatedComment_checker.go @@ -11,7 +11,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "deprecatedComment" - info.Tags = []string{"diagnostic"} + info.Tags = []string{linter.DiagnosticTag} info.Summary = "Detects malformed 'deprecated' doc-comments" info.Before = ` // deprecated, use FuncNew instead diff --git a/vendor/github.com/go-critic/go-critic/checkers/docStub_checker.go b/vendor/github.com/go-critic/go-critic/checkers/docStub_checker.go index 738544f3..aa23de42 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/docStub_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/docStub_checker.go @@ -13,7 +13,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "docStub" - info.Tags = []string{"style", "experimental"} + info.Tags = []string{linter.StyleTag, linter.ExperimentalTag} info.Summary = "Detects comments that silence go lint complaints about doc-comment" info.Before = ` // Foo ... diff --git a/vendor/github.com/go-critic/go-critic/checkers/dupBranchBody_checker.go b/vendor/github.com/go-critic/go-critic/checkers/dupBranchBody_checker.go index 734e8948..c4f01838 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/dupBranchBody_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/dupBranchBody_checker.go @@ -12,7 +12,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "dupBranchBody" - info.Tags = []string{"diagnostic"} + info.Tags = []string{linter.DiagnosticTag} info.Summary = "Detects duplicated branch bodies inside conditional statements" info.Before = ` if cond { diff --git a/vendor/github.com/go-critic/go-critic/checkers/dupCase_checker.go b/vendor/github.com/go-critic/go-critic/checkers/dupCase_checker.go index 9e36d995..381bad68 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/dupCase_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/dupCase_checker.go @@ -11,7 +11,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "dupCase" - info.Tags = []string{"diagnostic"} + info.Tags = []string{linter.DiagnosticTag} info.Summary = "Detects duplicated case clauses inside switch or select statements" info.Before = ` switch x { diff --git a/vendor/github.com/go-critic/go-critic/checkers/dupImports_checker.go b/vendor/github.com/go-critic/go-critic/checkers/dupImports_checker.go index 8f55d0bd..19079871 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/dupImports_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/dupImports_checker.go @@ -10,7 +10,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "dupImport" - info.Tags = []string{"style", "experimental"} + info.Tags = []string{linter.StyleTag, linter.ExperimentalTag} info.Summary = "Detects multiple imports of the same package under different aliases" info.Before = ` import ( diff --git a/vendor/github.com/go-critic/go-critic/checkers/dupSubExpr_checker.go b/vendor/github.com/go-critic/go-critic/checkers/dupSubExpr_checker.go index 35160919..9ab75945 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/dupSubExpr_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/dupSubExpr_checker.go @@ -15,7 +15,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "dupSubExpr" - info.Tags = []string{"diagnostic"} + info.Tags = []string{linter.DiagnosticTag} info.Summary = "Detects suspicious duplicated sub-expressions" info.Before = ` sort.Slice(xs, func(i, j int) bool { diff --git a/vendor/github.com/go-critic/go-critic/checkers/elseif_checker.go b/vendor/github.com/go-critic/go-critic/checkers/elseif_checker.go index 5cd9a3e4..857d09fa 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/elseif_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/elseif_checker.go @@ -12,7 +12,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "elseif" - info.Tags = []string{"style"} + info.Tags = []string{linter.StyleTag} info.Params = linter.CheckerParams{ "skipBalanced": { Value: true, diff --git a/vendor/github.com/go-critic/go-critic/checkers/embedded_rules.go b/vendor/github.com/go-critic/go-critic/checkers/embedded_rules.go index 7be115fe..ad507425 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/embedded_rules.go +++ b/vendor/github.com/go-critic/go-critic/checkers/embedded_rules.go @@ -101,6 +101,7 @@ func (c *embeddedRuleguardChecker) WalkFile(f *ast.File) { Pkg: c.ctx.Pkg, Types: c.ctx.TypesInfo, Sizes: c.ctx.SizesInfo, + GoVersion: ruleguard.GoVersion(c.ctx.GoVersion), Fset: c.ctx.FileSet, TruncateLen: 100, }) diff --git a/vendor/github.com/go-critic/go-critic/checkers/emptyFallthrough_checker.go b/vendor/github.com/go-critic/go-critic/checkers/emptyFallthrough_checker.go index 1b2f58c9..a008c618 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/emptyFallthrough_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/emptyFallthrough_checker.go @@ -11,7 +11,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "emptyFallthrough" - info.Tags = []string{"style", "experimental"} + info.Tags = []string{linter.StyleTag, linter.ExperimentalTag} info.Summary = "Detects fallthrough that can be avoided by using multi case values" info.Before = `switch kind { case reflect.Int: diff --git a/vendor/github.com/go-critic/go-critic/checkers/evalOrder_checker.go b/vendor/github.com/go-critic/go-critic/checkers/evalOrder_checker.go index 73659704..f8c5ae54 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/evalOrder_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/evalOrder_checker.go @@ -17,7 +17,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "evalOrder" - info.Tags = []string{"diagnostic", "experimental"} + info.Tags = []string{linter.DiagnosticTag, linter.ExperimentalTag} info.Summary = "Detects unwanted dependencies on the evaluation order" info.Before = `return x, f(&x)` info.After = ` diff --git a/vendor/github.com/go-critic/go-critic/checkers/exitAfterDefer_checker.go b/vendor/github.com/go-critic/go-critic/checkers/exitAfterDefer_checker.go index 2781dc26..9889f48e 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/exitAfterDefer_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/exitAfterDefer_checker.go @@ -14,7 +14,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "exitAfterDefer" - info.Tags = []string{"diagnostic"} + info.Tags = []string{linter.DiagnosticTag} info.Summary = "Detects calls to exit/fatal inside functions that use defer" info.Before = ` defer os.Remove(filename) diff --git a/vendor/github.com/go-critic/go-critic/checkers/filepathJoin_checker.go b/vendor/github.com/go-critic/go-critic/checkers/filepathJoin_checker.go index 6d62ef2b..17ab0ea8 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/filepathJoin_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/filepathJoin_checker.go @@ -13,7 +13,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "filepathJoin" - info.Tags = []string{"diagnostic", "experimental"} + info.Tags = []string{linter.DiagnosticTag, linter.ExperimentalTag} info.Summary = "Detects problems in filepath.Join() function calls" info.Before = `filepath.Join("dir/", filename)` info.After = `filepath.Join("dir", filename)` diff --git a/vendor/github.com/go-critic/go-critic/checkers/flagName_checker.go b/vendor/github.com/go-critic/go-critic/checkers/flagName_checker.go index e7040102..98b76e26 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/flagName_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/flagName_checker.go @@ -15,7 +15,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "flagName" - info.Tags = []string{"diagnostic"} + info.Tags = []string{linter.DiagnosticTag} info.Summary = "Detects suspicious flag names" info.Before = `b := flag.Bool(" foo ", false, "description")` info.After = `b := flag.Bool("foo", false, "description")` diff --git a/vendor/github.com/go-critic/go-critic/checkers/hexLiteral_checker.go b/vendor/github.com/go-critic/go-critic/checkers/hexLiteral_checker.go index fead716c..7301bd32 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/hexLiteral_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/hexLiteral_checker.go @@ -14,7 +14,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "hexLiteral" - info.Tags = []string{"style", "experimental"} + info.Tags = []string{linter.StyleTag, linter.ExperimentalTag} info.Summary = "Detects hex literals that have mixed case letter digits" info.Before = ` x := 0X12 diff --git a/vendor/github.com/go-critic/go-critic/checkers/hugeParam_checker.go b/vendor/github.com/go-critic/go-critic/checkers/hugeParam_checker.go index 07a1a22e..3b7f1d12 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/hugeParam_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/hugeParam_checker.go @@ -10,7 +10,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "hugeParam" - info.Tags = []string{"performance"} + info.Tags = []string{linter.PerformanceTag} info.Params = linter.CheckerParams{ "sizeThreshold": { Value: 80, diff --git a/vendor/github.com/go-critic/go-critic/checkers/ifElseChain_checker.go b/vendor/github.com/go-critic/go-critic/checkers/ifElseChain_checker.go index b36f1f13..e73c609d 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/ifElseChain_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/ifElseChain_checker.go @@ -10,7 +10,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "ifElseChain" - info.Tags = []string{"style"} + info.Tags = []string{linter.StyleTag} info.Params = linter.CheckerParams{ "minThreshold": { Value: 2, diff --git a/vendor/github.com/go-critic/go-critic/checkers/importShadow_checker.go b/vendor/github.com/go-critic/go-critic/checkers/importShadow_checker.go index 76a11e3b..b690487b 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/importShadow_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/importShadow_checker.go @@ -11,7 +11,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "importShadow" - info.Tags = []string{"style", "opinionated"} + info.Tags = []string{linter.StyleTag, linter.OpinionatedTag} info.Summary = "Detects when imported package names shadowed in the assignments" info.Before = ` // "path/filepath" is imported. diff --git a/vendor/github.com/go-critic/go-critic/checkers/initClause_checker.go b/vendor/github.com/go-critic/go-critic/checkers/initClause_checker.go index 8a3182f5..8612717b 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/initClause_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/initClause_checker.go @@ -12,7 +12,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "initClause" - info.Tags = []string{"style", "opinionated", "experimental"} + info.Tags = []string{linter.StyleTag, linter.OpinionatedTag, linter.ExperimentalTag} info.Summary = "Detects non-assignment statements inside if/switch init clause" info.Before = `if sideEffect(); cond { }` diff --git a/vendor/github.com/go-critic/go-critic/checkers/mapKey_checker.go b/vendor/github.com/go-critic/go-critic/checkers/mapKey_checker.go index 596ff871..ebc61c12 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/mapKey_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/mapKey_checker.go @@ -17,7 +17,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "mapKey" - info.Tags = []string{"diagnostic"} + info.Tags = []string{linter.DiagnosticTag} info.Summary = "Detects suspicious map literal keys" info.Before = ` _ = map[string]int{ diff --git a/vendor/github.com/go-critic/go-critic/checkers/methodExprCall_checker.go b/vendor/github.com/go-critic/go-critic/checkers/methodExprCall_checker.go index 4206d4e2..755d3b47 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/methodExprCall_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/methodExprCall_checker.go @@ -15,7 +15,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "methodExprCall" - info.Tags = []string{"style", "experimental"} + info.Tags = []string{linter.StyleTag, linter.ExperimentalTag} info.Summary = "Detects method expression call that can be replaced with a method call" info.Before = `f := foo{} foo.bar(f)` diff --git a/vendor/github.com/go-critic/go-critic/checkers/nestingReduce_checker.go b/vendor/github.com/go-critic/go-critic/checkers/nestingReduce_checker.go index 86b86960..dfe73018 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/nestingReduce_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/nestingReduce_checker.go @@ -10,7 +10,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "nestingReduce" - info.Tags = []string{"style", "opinionated", "experimental"} + info.Tags = []string{linter.StyleTag, linter.OpinionatedTag, linter.ExperimentalTag} info.Params = linter.CheckerParams{ "bodyWidth": { Value: 5, diff --git a/vendor/github.com/go-critic/go-critic/checkers/newDeref_checker.go b/vendor/github.com/go-critic/go-critic/checkers/newDeref_checker.go index fb50283b..1a1b05e0 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/newDeref_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/newDeref_checker.go @@ -15,7 +15,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "newDeref" - info.Tags = []string{"style"} + info.Tags = []string{linter.StyleTag} info.Summary = "Detects immediate dereferencing of `new` expressions" info.Before = `x := *new(bool)` info.After = `x := false` diff --git a/vendor/github.com/go-critic/go-critic/checkers/nilValReturn_checker.go b/vendor/github.com/go-critic/go-critic/checkers/nilValReturn_checker.go index c3539cb4..9a1213f5 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/nilValReturn_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/nilValReturn_checker.go @@ -14,7 +14,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "nilValReturn" - info.Tags = []string{"diagnostic", "experimental"} + info.Tags = []string{linter.DiagnosticTag, linter.ExperimentalTag} info.Summary = "Detects return statements those results evaluate to nil" info.Before = ` if err == nil { diff --git a/vendor/github.com/go-critic/go-critic/checkers/octalLiteral_checker.go b/vendor/github.com/go-critic/go-critic/checkers/octalLiteral_checker.go index b2c2519e..a25fac85 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/octalLiteral_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/octalLiteral_checker.go @@ -15,7 +15,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "octalLiteral" - info.Tags = []string{"style", "experimental", "opinionated"} + info.Tags = []string{linter.StyleTag, linter.ExperimentalTag, linter.OpinionatedTag} info.Summary = "Detects old-style octal literals" info.Before = `foo(02)` info.After = `foo(0o2)` @@ -31,6 +31,9 @@ type octalLiteralChecker struct { } func (c *octalLiteralChecker) VisitExpr(expr ast.Expr) { + if !c.ctx.GoVersion.GreaterOrEqual(linter.GoVersion{Major: 1, Minor: 13}) { + return + } lit := astcast.ToBasicLit(expr) if lit.Kind != token.INT { return diff --git a/vendor/github.com/go-critic/go-critic/checkers/paramTypeCombine_checker.go b/vendor/github.com/go-critic/go-critic/checkers/paramTypeCombine_checker.go index 1f76ab6a..c777fec9 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/paramTypeCombine_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/paramTypeCombine_checker.go @@ -13,7 +13,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "paramTypeCombine" - info.Tags = []string{"style", "opinionated"} + info.Tags = []string{linter.StyleTag, linter.OpinionatedTag} info.Summary = "Detects if function parameters could be combined by type and suggest the way to do it" info.Before = `func foo(a, b int, c, d int, e, f int, g int) {}` info.After = `func foo(a, b, c, d, e, f, g int) {}` @@ -47,6 +47,7 @@ func (c *paramTypeCombineChecker) optimizeFuncType(f *ast.FuncType) *ast.FuncTyp return optimizedParamFunc } + func (c *paramTypeCombineChecker) optimizeParams(params *ast.FieldList) *ast.FieldList { // To avoid false positives, skip unnamed param lists. // @@ -72,8 +73,7 @@ func (c *paramTypeCombineChecker) optimizeParams(params *ast.FieldList) *ast.Fie names = make([]*ast.Ident, len(p.Names)) copy(names, p.Names) if astequal.Expr(p.Type, params.List[i].Type) { - list[len(list)-1].Names = - append(list[len(list)-1].Names, names...) + list[len(list)-1].Names = append(list[len(list)-1].Names, names...) } else { list = append(list, &ast.Field{ Names: names, diff --git a/vendor/github.com/go-critic/go-critic/checkers/ptrToRefParam_checker.go b/vendor/github.com/go-critic/go-critic/checkers/ptrToRefParam_checker.go index 6b869338..172a4acb 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/ptrToRefParam_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/ptrToRefParam_checker.go @@ -11,7 +11,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "ptrToRefParam" - info.Tags = []string{"style", "opinionated", "experimental"} + info.Tags = []string{linter.StyleTag, linter.OpinionatedTag, linter.ExperimentalTag} info.Summary = "Detects input and output parameters that have a type of pointer to referential type" info.Before = `func f(m *map[string]int) (*chan *int)` info.After = `func f(m map[string]int) (chan *int)` diff --git a/vendor/github.com/go-critic/go-critic/checkers/rangeExprCopy_checker.go b/vendor/github.com/go-critic/go-critic/checkers/rangeExprCopy_checker.go index 8adc16be..3f61ee0b 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/rangeExprCopy_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/rangeExprCopy_checker.go @@ -11,7 +11,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "rangeExprCopy" - info.Tags = []string{"performance"} + info.Tags = []string{linter.PerformanceTag} info.Params = linter.CheckerParams{ "sizeThreshold": { Value: 512, diff --git a/vendor/github.com/go-critic/go-critic/checkers/rangeValCopy_checker.go b/vendor/github.com/go-critic/go-critic/checkers/rangeValCopy_checker.go index e1f76fd4..6d15c30c 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/rangeValCopy_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/rangeValCopy_checker.go @@ -10,7 +10,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "rangeValCopy" - info.Tags = []string{"performance"} + info.Tags = []string{linter.PerformanceTag} info.Params = linter.CheckerParams{ "sizeThreshold": { Value: 128, diff --git a/vendor/github.com/go-critic/go-critic/checkers/regexpPattern_checker.go b/vendor/github.com/go-critic/go-critic/checkers/regexpPattern_checker.go index 5d57c849..45aba261 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/regexpPattern_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/regexpPattern_checker.go @@ -13,7 +13,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "regexpPattern" - info.Tags = []string{"diagnostic", "experimental"} + info.Tags = []string{linter.DiagnosticTag, linter.ExperimentalTag} info.Summary = "Detects suspicious regexp patterns" info.Before = "regexp.MustCompile(`google.com|yandex.ru`)" info.After = "regexp.MustCompile(`google\\.com|yandex\\.ru`)" diff --git a/vendor/github.com/go-critic/go-critic/checkers/regexpSimplify_checker.go b/vendor/github.com/go-critic/go-critic/checkers/regexpSimplify_checker.go index 5c5e54a6..f500f435 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/regexpSimplify_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/regexpSimplify_checker.go @@ -17,7 +17,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "regexpSimplify" - info.Tags = []string{"style", "experimental", "opinionated"} + info.Tags = []string{linter.StyleTag, linter.ExperimentalTag, linter.OpinionatedTag} info.Summary = "Detects regexp patterns that can be simplified" info.Before = "regexp.MustCompile(`(?:a|b|c) [a-z][a-z]*`)" info.After = "regexp.MustCompile(`[abc] {3}[a-z]+`)" diff --git a/vendor/github.com/go-critic/go-critic/checkers/ruleguard_checker.go b/vendor/github.com/go-critic/go-critic/checkers/ruleguard_checker.go index 769ebad7..29723a69 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/ruleguard_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/ruleguard_checker.go @@ -20,7 +20,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "ruleguard" - info.Tags = []string{"style", "experimental"} + info.Tags = []string{linter.StyleTag, linter.ExperimentalTag} info.Params = linter.CheckerParams{ "rules": { Value: "", @@ -84,7 +84,7 @@ func newErrorHandler(failOnErrorFlag string) (*parseErrorHandler, error) { h := parseErrorHandler{ failureConditions: make(map[string]func(err error) bool), } - var failOnErrorPredicates = map[string]func(error) bool{ + failOnErrorPredicates := map[string]func(error) bool{ "dsl": func(err error) bool { var e *ruleguard.ImportError; return !errors.As(err, &e) }, "import": func(err error) bool { var e *ruleguard.ImportError; return errors.As(err, &e) }, "all": func(err error) bool { return true }, @@ -160,8 +160,8 @@ func newRuleguardChecker(info *linter.CheckerInfo, ctx *linter.CheckerContext) ( } } - if !enabledTags["experimental"] { - disabledTags["experimental"] = true + if !enabledTags[linter.ExperimentalTag] { + disabledTags[linter.ExperimentalTag] = true } ruleguardDebug := os.Getenv("GOCRITIC_RULEGUARD_DEBUG") != "" diff --git a/vendor/github.com/go-critic/go-critic/checkers/rulesdata/rulesdata.go b/vendor/github.com/go-critic/go-critic/checkers/rulesdata/rulesdata.go index 21ed2b07..503118c7 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/rulesdata/rulesdata.go +++ b/vendor/github.com/go-critic/go-critic/checkers/rulesdata/rulesdata.go @@ -61,37 +61,20 @@ var PrecompiledRules = &ir.File{ {Line: 17, Value: "fmt.Sprintf(\"%s\", $x)"}, {Line: 17, Value: "fmt.Sprintf(\"%v\", $x)"}, }, - ReportTemplate: "use $x.Error() instead", - SuggestTemplate: "$x.Error()", - WhereExpr: ir.FilterExpr{ - Line: 18, - Op: ir.FilterVarTypeImplementsOp, - Src: "m[\"x\"].Type.Implements(`error`)", - Value: "x", - Args: []ir.FilterExpr{{Line: 18, Op: ir.FilterStringOp, Src: "`error`", Value: "error"}}, - }, - }, - { - Line: 22, - SyntaxPatterns: []ir.PatternString{ - {Line: 22, Value: "fmt.Sprint($x)"}, - {Line: 22, Value: "fmt.Sprintf(\"%s\", $x)"}, - {Line: 22, Value: "fmt.Sprintf(\"%v\", $x)"}, - }, ReportTemplate: "$x is already string", SuggestTemplate: "$x", WhereExpr: ir.FilterExpr{ - Line: 23, + Line: 18, Op: ir.FilterVarTypeIsOp, Src: "m[\"x\"].Type.Is(`string`)", Value: "x", - Args: []ir.FilterExpr{{Line: 23, Op: ir.FilterStringOp, Src: "`string`", Value: "string"}}, + Args: []ir.FilterExpr{{Line: 18, Op: ir.FilterStringOp, Src: "`string`", Value: "string"}}, }, }, }, }, { - Line: 32, + Line: 27, Name: "deferUnlambda", MatcherName: "m", DocTags: []string{"style", "experimental"}, @@ -100,55 +83,55 @@ var PrecompiledRules = &ir.File{ DocAfter: "defer f()", Rules: []ir.Rule{ { - Line: 33, - SyntaxPatterns: []ir.PatternString{{Line: 33, Value: "defer func() { $f($*args) }()"}}, + Line: 28, + SyntaxPatterns: []ir.PatternString{{Line: 28, Value: "defer func() { $f($*args) }()"}}, ReportTemplate: "can rewrite as `defer $f($args)`", WhereExpr: ir.FilterExpr{ - Line: 34, + Line: 29, Op: ir.FilterAndOp, Src: "m[\"f\"].Node.Is(`Ident`) && m[\"f\"].Text != \"panic\" && m[\"f\"].Text != \"recover\" && m[\"args\"].Const", Args: []ir.FilterExpr{ { - Line: 34, + Line: 29, Op: ir.FilterAndOp, Src: "m[\"f\"].Node.Is(`Ident`) && m[\"f\"].Text != \"panic\" && m[\"f\"].Text != \"recover\"", Args: []ir.FilterExpr{ { - Line: 34, + Line: 29, Op: ir.FilterAndOp, Src: "m[\"f\"].Node.Is(`Ident`) && m[\"f\"].Text != \"panic\"", Args: []ir.FilterExpr{ { - Line: 34, + Line: 29, Op: ir.FilterVarNodeIsOp, Src: "m[\"f\"].Node.Is(`Ident`)", Value: "f", - Args: []ir.FilterExpr{{Line: 34, Op: ir.FilterStringOp, Src: "`Ident`", Value: "Ident"}}, + Args: []ir.FilterExpr{{Line: 29, Op: ir.FilterStringOp, Src: "`Ident`", Value: "Ident"}}, }, { - Line: 34, + Line: 29, Op: ir.FilterNeqOp, Src: "m[\"f\"].Text != \"panic\"", Args: []ir.FilterExpr{ - {Line: 34, Op: ir.FilterVarTextOp, Src: "m[\"f\"].Text", Value: "f"}, - {Line: 34, Op: ir.FilterStringOp, Src: "\"panic\"", Value: "panic"}, + {Line: 29, Op: ir.FilterVarTextOp, Src: "m[\"f\"].Text", Value: "f"}, + {Line: 29, Op: ir.FilterStringOp, Src: "\"panic\"", Value: "panic"}, }, }, }, }, { - Line: 34, + Line: 29, Op: ir.FilterNeqOp, Src: "m[\"f\"].Text != \"recover\"", Args: []ir.FilterExpr{ - {Line: 34, Op: ir.FilterVarTextOp, Src: "m[\"f\"].Text", Value: "f"}, - {Line: 34, Op: ir.FilterStringOp, Src: "\"recover\"", Value: "recover"}, + {Line: 29, Op: ir.FilterVarTextOp, Src: "m[\"f\"].Text", Value: "f"}, + {Line: 29, Op: ir.FilterStringOp, Src: "\"recover\"", Value: "recover"}, }, }, }, }, { - Line: 34, + Line: 29, Op: ir.FilterVarConstOp, Src: "m[\"args\"].Const", Value: "args", @@ -157,28 +140,28 @@ var PrecompiledRules = &ir.File{ }, }, { - Line: 37, - SyntaxPatterns: []ir.PatternString{{Line: 37, Value: "defer func() { $pkg.$f($*args) }()"}}, + Line: 32, + SyntaxPatterns: []ir.PatternString{{Line: 32, Value: "defer func() { $pkg.$f($*args) }()"}}, ReportTemplate: "can rewrite as `defer $pkg.$f($args)`", WhereExpr: ir.FilterExpr{ - Line: 38, + Line: 33, Op: ir.FilterAndOp, Src: "m[\"f\"].Node.Is(`Ident`) && m[\"args\"].Const && m[\"pkg\"].Object.Is(`PkgName`)", Args: []ir.FilterExpr{ { - Line: 38, + Line: 33, Op: ir.FilterAndOp, Src: "m[\"f\"].Node.Is(`Ident`) && m[\"args\"].Const", Args: []ir.FilterExpr{ { - Line: 38, + Line: 33, Op: ir.FilterVarNodeIsOp, Src: "m[\"f\"].Node.Is(`Ident`)", Value: "f", - Args: []ir.FilterExpr{{Line: 38, Op: ir.FilterStringOp, Src: "`Ident`", Value: "Ident"}}, + Args: []ir.FilterExpr{{Line: 33, Op: ir.FilterStringOp, Src: "`Ident`", Value: "Ident"}}, }, { - Line: 38, + Line: 33, Op: ir.FilterVarConstOp, Src: "m[\"args\"].Const", Value: "args", @@ -186,11 +169,11 @@ var PrecompiledRules = &ir.File{ }, }, { - Line: 38, + Line: 33, Op: ir.FilterVarObjectIsOp, Src: "m[\"pkg\"].Object.Is(`PkgName`)", Value: "pkg", - Args: []ir.FilterExpr{{Line: 38, Op: ir.FilterStringOp, Src: "`PkgName`", Value: "PkgName"}}, + Args: []ir.FilterExpr{{Line: 33, Op: ir.FilterStringOp, Src: "`PkgName`", Value: "PkgName"}}, }, }, }, @@ -198,7 +181,7 @@ var PrecompiledRules = &ir.File{ }, }, { - Line: 46, + Line: 41, Name: "badLock", MatcherName: "m", DocTags: []string{"diagnostic", "experimental"}, @@ -207,91 +190,91 @@ var PrecompiledRules = &ir.File{ DocAfter: "mu.Lock(); defer mu.Unlock()", Rules: []ir.Rule{ { - Line: 50, - SyntaxPatterns: []ir.PatternString{{Line: 50, Value: "$mu1.Lock(); $mu2.Unlock()"}}, + Line: 45, + SyntaxPatterns: []ir.PatternString{{Line: 45, Value: "$mu1.Lock(); $mu2.Unlock()"}}, ReportTemplate: "defer is missing, mutex is unlocked immediately", WhereExpr: ir.FilterExpr{ - Line: 51, + Line: 46, Op: ir.FilterEqOp, Src: "m[\"mu1\"].Text == m[\"mu2\"].Text", Args: []ir.FilterExpr{ - {Line: 51, Op: ir.FilterVarTextOp, Src: "m[\"mu1\"].Text", Value: "mu1"}, - {Line: 51, Op: ir.FilterVarTextOp, Src: "m[\"mu2\"].Text", Value: "mu2"}, + {Line: 46, Op: ir.FilterVarTextOp, Src: "m[\"mu1\"].Text", Value: "mu1"}, + {Line: 46, Op: ir.FilterVarTextOp, Src: "m[\"mu2\"].Text", Value: "mu2"}, }, }, LocationVar: "mu2", }, { - Line: 55, - SyntaxPatterns: []ir.PatternString{{Line: 55, Value: "$mu1.RLock(); $mu2.RUnlock()"}}, + Line: 50, + SyntaxPatterns: []ir.PatternString{{Line: 50, Value: "$mu1.RLock(); $mu2.RUnlock()"}}, ReportTemplate: "defer is missing, mutex is unlocked immediately", WhereExpr: ir.FilterExpr{ - Line: 56, + Line: 51, Op: ir.FilterEqOp, Src: "m[\"mu1\"].Text == m[\"mu2\"].Text", Args: []ir.FilterExpr{ - {Line: 56, Op: ir.FilterVarTextOp, Src: "m[\"mu1\"].Text", Value: "mu1"}, - {Line: 56, Op: ir.FilterVarTextOp, Src: "m[\"mu2\"].Text", Value: "mu2"}, + {Line: 51, Op: ir.FilterVarTextOp, Src: "m[\"mu1\"].Text", Value: "mu1"}, + {Line: 51, Op: ir.FilterVarTextOp, Src: "m[\"mu2\"].Text", Value: "mu2"}, }, }, LocationVar: "mu2", }, { - Line: 61, - SyntaxPatterns: []ir.PatternString{{Line: 61, Value: "$mu1.Lock(); defer $mu2.RUnlock()"}}, + Line: 56, + SyntaxPatterns: []ir.PatternString{{Line: 56, Value: "$mu1.Lock(); defer $mu2.RUnlock()"}}, ReportTemplate: "suspicious unlock, maybe Unlock was intended?", WhereExpr: ir.FilterExpr{ - Line: 62, + Line: 57, Op: ir.FilterEqOp, Src: "m[\"mu1\"].Text == m[\"mu2\"].Text", Args: []ir.FilterExpr{ - {Line: 62, Op: ir.FilterVarTextOp, Src: "m[\"mu1\"].Text", Value: "mu1"}, - {Line: 62, Op: ir.FilterVarTextOp, Src: "m[\"mu2\"].Text", Value: "mu2"}, + {Line: 57, Op: ir.FilterVarTextOp, Src: "m[\"mu1\"].Text", Value: "mu1"}, + {Line: 57, Op: ir.FilterVarTextOp, Src: "m[\"mu2\"].Text", Value: "mu2"}, }, }, LocationVar: "mu2", }, { - Line: 66, - SyntaxPatterns: []ir.PatternString{{Line: 66, Value: "$mu1.RLock(); defer $mu2.Unlock()"}}, + Line: 61, + SyntaxPatterns: []ir.PatternString{{Line: 61, Value: "$mu1.RLock(); defer $mu2.Unlock()"}}, ReportTemplate: "suspicious unlock, maybe RUnlock was intended?", WhereExpr: ir.FilterExpr{ - Line: 67, + Line: 62, Op: ir.FilterEqOp, Src: "m[\"mu1\"].Text == m[\"mu2\"].Text", Args: []ir.FilterExpr{ - {Line: 67, Op: ir.FilterVarTextOp, Src: "m[\"mu1\"].Text", Value: "mu1"}, - {Line: 67, Op: ir.FilterVarTextOp, Src: "m[\"mu2\"].Text", Value: "mu2"}, + {Line: 62, Op: ir.FilterVarTextOp, Src: "m[\"mu1\"].Text", Value: "mu1"}, + {Line: 62, Op: ir.FilterVarTextOp, Src: "m[\"mu2\"].Text", Value: "mu2"}, }, }, LocationVar: "mu2", }, { - Line: 72, - SyntaxPatterns: []ir.PatternString{{Line: 72, Value: "$mu1.Lock(); defer $mu2.Lock()"}}, + Line: 67, + SyntaxPatterns: []ir.PatternString{{Line: 67, Value: "$mu1.Lock(); defer $mu2.Lock()"}}, ReportTemplate: "maybe defer $mu1.Unlock() was intended?", WhereExpr: ir.FilterExpr{ - Line: 73, + Line: 68, Op: ir.FilterEqOp, Src: "m[\"mu1\"].Text == m[\"mu2\"].Text", Args: []ir.FilterExpr{ - {Line: 73, Op: ir.FilterVarTextOp, Src: "m[\"mu1\"].Text", Value: "mu1"}, - {Line: 73, Op: ir.FilterVarTextOp, Src: "m[\"mu2\"].Text", Value: "mu2"}, + {Line: 68, Op: ir.FilterVarTextOp, Src: "m[\"mu1\"].Text", Value: "mu1"}, + {Line: 68, Op: ir.FilterVarTextOp, Src: "m[\"mu2\"].Text", Value: "mu2"}, }, }, LocationVar: "mu2", }, { - Line: 77, - SyntaxPatterns: []ir.PatternString{{Line: 77, Value: "$mu1.RLock(); defer $mu2.RLock()"}}, + Line: 72, + SyntaxPatterns: []ir.PatternString{{Line: 72, Value: "$mu1.RLock(); defer $mu2.RLock()"}}, ReportTemplate: "maybe defer $mu1.RUnlock() was intended?", WhereExpr: ir.FilterExpr{ - Line: 78, + Line: 73, Op: ir.FilterEqOp, Src: "m[\"mu1\"].Text == m[\"mu2\"].Text", Args: []ir.FilterExpr{ - {Line: 78, Op: ir.FilterVarTextOp, Src: "m[\"mu1\"].Text", Value: "mu1"}, - {Line: 78, Op: ir.FilterVarTextOp, Src: "m[\"mu2\"].Text", Value: "mu2"}, + {Line: 73, Op: ir.FilterVarTextOp, Src: "m[\"mu1\"].Text", Value: "mu1"}, + {Line: 73, Op: ir.FilterVarTextOp, Src: "m[\"mu2\"].Text", Value: "mu2"}, }, }, LocationVar: "mu2", @@ -299,7 +282,7 @@ var PrecompiledRules = &ir.File{ }, }, { - Line: 87, + Line: 82, Name: "httpNoBody", MatcherName: "m", DocTags: []string{"style", "experimental"}, @@ -308,10 +291,25 @@ var PrecompiledRules = &ir.File{ DocAfter: "http.NewRequest(\"GET\", url, http.NoBody)", Rules: []ir.Rule{ { - Line: 88, - SyntaxPatterns: []ir.PatternString{{Line: 88, Value: "http.NewRequest($method, $url, $nil)"}}, + Line: 83, + SyntaxPatterns: []ir.PatternString{{Line: 83, Value: "http.NewRequest($method, $url, $nil)"}}, ReportTemplate: "http.NoBody should be preferred to the nil request body", SuggestTemplate: "http.NewRequest($method, $url, http.NoBody)", + WhereExpr: ir.FilterExpr{ + Line: 84, + Op: ir.FilterEqOp, + Src: "m[\"nil\"].Text == \"nil\"", + Args: []ir.FilterExpr{ + {Line: 84, Op: ir.FilterVarTextOp, Src: "m[\"nil\"].Text", Value: "nil"}, + {Line: 84, Op: ir.FilterStringOp, Src: "\"nil\"", Value: "nil"}, + }, + }, + }, + { + Line: 88, + SyntaxPatterns: []ir.PatternString{{Line: 88, Value: "http.NewRequestWithContext($ctx, $method, $url, $nil)"}}, + ReportTemplate: "http.NoBody should be preferred to the nil request body", + SuggestTemplate: "http.NewRequestWithContext($ctx, $method, $url, http.NoBody)", WhereExpr: ir.FilterExpr{ Line: 89, Op: ir.FilterEqOp, @@ -324,9 +322,9 @@ var PrecompiledRules = &ir.File{ }, { Line: 93, - SyntaxPatterns: []ir.PatternString{{Line: 93, Value: "http.NewRequestWithContext($ctx, $method, $url, $nil)"}}, + SyntaxPatterns: []ir.PatternString{{Line: 93, Value: "httptest.NewRequest($method, $url, $nil)"}}, ReportTemplate: "http.NoBody should be preferred to the nil request body", - SuggestTemplate: "http.NewRequestWithContext($ctx, $method, $url, http.NoBody)", + SuggestTemplate: "httptest.NewRequest($method, $url, http.NoBody)", WhereExpr: ir.FilterExpr{ Line: 94, Op: ir.FilterEqOp, diff --git a/vendor/github.com/go-critic/go-critic/checkers/singleCaseSwitch_checker.go b/vendor/github.com/go-critic/go-critic/checkers/singleCaseSwitch_checker.go index 4de5d19e..a1a399fd 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/singleCaseSwitch_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/singleCaseSwitch_checker.go @@ -13,7 +13,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "singleCaseSwitch" - info.Tags = []string{"style"} + info.Tags = []string{linter.StyleTag} info.Summary = "Detects switch statements that could be better written as if statement" info.Before = ` switch x := x.(type) { diff --git a/vendor/github.com/go-critic/go-critic/checkers/sloppyReassign_checker.go b/vendor/github.com/go-critic/go-critic/checkers/sloppyReassign_checker.go index c80a1718..d83d7fd5 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/sloppyReassign_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/sloppyReassign_checker.go @@ -15,7 +15,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "sloppyReassign" - info.Tags = []string{"diagnostic", "experimental"} + info.Tags = []string{linter.DiagnosticTag, linter.ExperimentalTag} info.Summary = "Detects suspicious/confusing re-assignments" info.Before = `if err = f(); err != nil { return err }` info.After = `if err := f(); err != nil { return err }` diff --git a/vendor/github.com/go-critic/go-critic/checkers/sloppyTypeAssert_checker.go b/vendor/github.com/go-critic/go-critic/checkers/sloppyTypeAssert_checker.go index c3bc159f..454ab78b 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/sloppyTypeAssert_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/sloppyTypeAssert_checker.go @@ -13,7 +13,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "sloppyTypeAssert" - info.Tags = []string{"diagnostic"} + info.Tags = []string{linter.DiagnosticTag} info.Summary = "Detects redundant type assertions" info.Before = ` func f(r io.Reader) interface{} { diff --git a/vendor/github.com/go-critic/go-critic/checkers/sortSlice_checker.go b/vendor/github.com/go-critic/go-critic/checkers/sortSlice_checker.go index 4394f201..22ef3b16 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/sortSlice_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/sortSlice_checker.go @@ -17,7 +17,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "sortSlice" - info.Tags = []string{"diagnostic", "experimental"} + info.Tags = []string{linter.DiagnosticTag, linter.ExperimentalTag} info.Summary = "Detects suspicious sort.Slice calls" info.Before = `sort.Slice(xs, func(i, j) bool { return keys[i] < keys[j] })` info.After = `sort.Slice(kv, func(i, j) bool { return kv[i].key < kv[j].key })` diff --git a/vendor/github.com/go-critic/go-critic/checkers/sqlQuery_checker.go b/vendor/github.com/go-critic/go-critic/checkers/sqlQuery_checker.go index c1cf6bbb..8a132b58 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/sqlQuery_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/sqlQuery_checker.go @@ -13,7 +13,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "sqlQuery" - info.Tags = []string{"diagnostic", "experimental"} + info.Tags = []string{linter.DiagnosticTag, linter.ExperimentalTag} info.Summary = "Detects issue in Query() and Exec() calls" info.Before = `_, err := db.Query("UPDATE ...")` info.After = `_, err := db.Exec("UPDATE ...")` diff --git a/vendor/github.com/go-critic/go-critic/checkers/todoCommentWithoutDetail_checker.go b/vendor/github.com/go-critic/go-critic/checkers/todoCommentWithoutDetail_checker.go index ca2b6d23..f8e4b9b3 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/todoCommentWithoutDetail_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/todoCommentWithoutDetail_checker.go @@ -11,7 +11,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "todoCommentWithoutDetail" - info.Tags = []string{"style", "opinionated", "experimental"} + info.Tags = []string{linter.StyleTag, linter.OpinionatedTag, linter.ExperimentalTag} info.Summary = "Detects TODO comments without detail/assignee" info.Before = ` // TODO diff --git a/vendor/github.com/go-critic/go-critic/checkers/tooManyResults_checker.go b/vendor/github.com/go-critic/go-critic/checkers/tooManyResults_checker.go index 9527c57e..57411ba2 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/tooManyResults_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/tooManyResults_checker.go @@ -11,7 +11,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "tooManyResultsChecker" - info.Tags = []string{"style", "opinionated", "experimental"} + info.Tags = []string{linter.StyleTag, linter.OpinionatedTag, linter.ExperimentalTag} info.Params = linter.CheckerParams{ "maxResults": { Value: 5, diff --git a/vendor/github.com/go-critic/go-critic/checkers/truncateCmp_checker.go b/vendor/github.com/go-critic/go-critic/checkers/truncateCmp_checker.go index 34946d1e..b3690252 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/truncateCmp_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/truncateCmp_checker.go @@ -15,7 +15,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "truncateCmp" - info.Tags = []string{"diagnostic", "experimental"} + info.Tags = []string{linter.DiagnosticTag, linter.ExperimentalTag} info.Params = linter.CheckerParams{ "skipArchDependent": { Value: true, diff --git a/vendor/github.com/go-critic/go-critic/checkers/typeAssertChain_checker.go b/vendor/github.com/go-critic/go-critic/checkers/typeAssertChain_checker.go index 48757bcf..e0d20fd4 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/typeAssertChain_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/typeAssertChain_checker.go @@ -16,7 +16,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "typeAssertChain" - info.Tags = []string{"style", "experimental"} + info.Tags = []string{linter.StyleTag, linter.ExperimentalTag} info.Summary = "Detects repeated type assertions and suggests to replace them with type switch statement" info.Before = ` if x, ok := v.(T1); ok { diff --git a/vendor/github.com/go-critic/go-critic/checkers/typeDefFirst_checker.go b/vendor/github.com/go-critic/go-critic/checkers/typeDefFirst_checker.go index 2177001d..11381c40 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/typeDefFirst_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/typeDefFirst_checker.go @@ -11,7 +11,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "typeDefFirst" - info.Tags = []string{"style", "experimental"} + info.Tags = []string{linter.StyleTag, linter.ExperimentalTag} info.Summary = "Detects method declarations preceding the type definition itself" info.Before = ` func (r rec) Method() {} diff --git a/vendor/github.com/go-critic/go-critic/checkers/typeSwitchVar_checker.go b/vendor/github.com/go-critic/go-critic/checkers/typeSwitchVar_checker.go index b4339de9..4b27b179 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/typeSwitchVar_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/typeSwitchVar_checker.go @@ -14,7 +14,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "typeSwitchVar" - info.Tags = []string{"style"} + info.Tags = []string{linter.StyleTag} info.Summary = "Detects type switches that can benefit from type guard clause with variable" info.Before = ` switch v.(type) { diff --git a/vendor/github.com/go-critic/go-critic/checkers/typeUnparen_checker.go b/vendor/github.com/go-critic/go-critic/checkers/typeUnparen_checker.go index 8c6491c1..d270268b 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/typeUnparen_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/typeUnparen_checker.go @@ -13,7 +13,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "typeUnparen" - info.Tags = []string{"style", "opinionated"} + info.Tags = []string{linter.StyleTag, linter.OpinionatedTag} info.Summary = "Detects unneded parenthesis inside type expressions and suggests to remove them" info.Before = `type foo [](func([](func())))` info.After = `type foo []func([]func())` diff --git a/vendor/github.com/go-critic/go-critic/checkers/underef_checker.go b/vendor/github.com/go-critic/go-critic/checkers/underef_checker.go index e9d7cc6a..0ce2c89b 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/underef_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/underef_checker.go @@ -14,7 +14,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "underef" - info.Tags = []string{"style"} + info.Tags = []string{linter.StyleTag} info.Params = linter.CheckerParams{ "skipRecvDeref": { Value: true, diff --git a/vendor/github.com/go-critic/go-critic/checkers/unlabelStmt_checker.go b/vendor/github.com/go-critic/go-critic/checkers/unlabelStmt_checker.go index 38e689c7..d0e83f3c 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/unlabelStmt_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/unlabelStmt_checker.go @@ -12,7 +12,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "unlabelStmt" - info.Tags = []string{"style", "experimental"} + info.Tags = []string{linter.StyleTag, linter.ExperimentalTag} info.Summary = "Detects redundant statement labels" info.Before = ` derp: diff --git a/vendor/github.com/go-critic/go-critic/checkers/unlambda_checker.go b/vendor/github.com/go-critic/go-critic/checkers/unlambda_checker.go index 6e5ed59f..bcfe5a0c 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/unlambda_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/unlambda_checker.go @@ -17,7 +17,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "unlambda" - info.Tags = []string{"style"} + info.Tags = []string{linter.StyleTag} info.Summary = "Detects function literals that can be simplified" info.Before = `func(x int) int { return fn(x) }` info.After = `fn` diff --git a/vendor/github.com/go-critic/go-critic/checkers/unnamedResult_checker.go b/vendor/github.com/go-critic/go-critic/checkers/unnamedResult_checker.go index 5f1afbdc..0d40addf 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/unnamedResult_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/unnamedResult_checker.go @@ -11,7 +11,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "unnamedResult" - info.Tags = []string{"style", "opinionated", "experimental"} + info.Tags = []string{linter.StyleTag, linter.OpinionatedTag, linter.ExperimentalTag} info.Params = linter.CheckerParams{ "checkExported": { Value: false, diff --git a/vendor/github.com/go-critic/go-critic/checkers/unnecessaryBlock_checker.go b/vendor/github.com/go-critic/go-critic/checkers/unnecessaryBlock_checker.go index b1d1728b..b577ff42 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/unnecessaryBlock_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/unnecessaryBlock_checker.go @@ -13,7 +13,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "unnecessaryBlock" - info.Tags = []string{"style", "opinionated", "experimental"} + info.Tags = []string{linter.StyleTag, linter.OpinionatedTag, linter.ExperimentalTag} info.Summary = "Detects unnecessary braced statement blocks" info.Before = ` x := 1 diff --git a/vendor/github.com/go-critic/go-critic/checkers/unnecessaryDefer_checker.go b/vendor/github.com/go-critic/go-critic/checkers/unnecessaryDefer_checker.go index a3863e6c..4358ab17 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/unnecessaryDefer_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/unnecessaryDefer_checker.go @@ -12,7 +12,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "unnecessaryDefer" - info.Tags = []string{"diagnostic", "experimental"} + info.Tags = []string{linter.DiagnosticTag, linter.ExperimentalTag} info.Summary = "Detects redundantly deferred calls" info.Before = ` func() { diff --git a/vendor/github.com/go-critic/go-critic/checkers/weakCond_checker.go b/vendor/github.com/go-critic/go-critic/checkers/weakCond_checker.go index 42f63e22..3d7c9c12 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/weakCond_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/weakCond_checker.go @@ -17,7 +17,7 @@ import ( func init() { var info linter.CheckerInfo info.Name = "weakCond" - info.Tags = []string{"diagnostic", "experimental"} + info.Tags = []string{linter.DiagnosticTag, linter.ExperimentalTag} info.Summary = "Detects conditions that are unsafe due to not being exhaustive" info.Before = `xs != nil && xs[0] != nil` info.After = `len(xs) != 0 && xs[0] != nil` diff --git a/vendor/github.com/go-critic/go-critic/checkers/whyNoLint_checker.go b/vendor/github.com/go-critic/go-critic/checkers/whyNoLint_checker.go index 4840018f..eaa53e5d 100644 --- a/vendor/github.com/go-critic/go-critic/checkers/whyNoLint_checker.go +++ b/vendor/github.com/go-critic/go-critic/checkers/whyNoLint_checker.go @@ -10,13 +10,12 @@ import ( ) func init() { - info := linter.CheckerInfo{ - Name: "whyNoLint", - Tags: []string{"style", "experimental"}, - Summary: "Ensures that `//nolint` comments include an explanation", - Before: `//nolint`, - After: `//nolint // reason`, - } + var info linter.CheckerInfo + info.Name = "whyNoLint" + info.Tags = []string{linter.StyleTag, linter.ExperimentalTag} + info.Summary = "Ensures that `//nolint` comments include an explanation" + info.Before = `//nolint` + info.After = `//nolint // reason` collection.AddChecker(&info, func(ctx *linter.CheckerContext) (linter.FileWalker, error) { return astwalk.WalkerForComment(&whyNoLintChecker{ diff --git a/vendor/github.com/go-critic/go-critic/linter/linter.go b/vendor/github.com/go-critic/go-critic/linter/linter.go index 12f0a1c5..27e9b659 100644 --- a/vendor/github.com/go-critic/go-critic/linter/linter.go +++ b/vendor/github.com/go-critic/go-critic/linter/linter.go @@ -9,6 +9,15 @@ import ( "github.com/go-toolsmith/astfmt" ) +const ( + DiagnosticTag = "diagnostic" + ExperimentalTag = "experimental" + OpinionatedTag = "opinionated" + PerformanceTag = "performance" + SecurityTag = "security" + StyleTag = "style" +) + // UnknownType is a special sentinel value that is returned from the CheckerContext.TypeOf // method instead of the nil type. var UnknownType types.Type = types.Typ[types.Invalid] diff --git a/vendor/github.com/golangci/golangci-lint/internal/errorutil/errors.go b/vendor/github.com/golangci/golangci-lint/internal/errorutil/errors.go index 5cb86d66..c8a3a035 100644 --- a/vendor/github.com/golangci/golangci-lint/internal/errorutil/errors.go +++ b/vendor/github.com/golangci/golangci-lint/internal/errorutil/errors.go @@ -6,11 +6,11 @@ import ( // PanicError can be used to not print stacktrace twice type PanicError struct { - recovered interface{} + recovered any stack []byte } -func NewPanicError(recovered interface{}, stack []byte) *PanicError { +func NewPanicError(recovered any, stack []byte) *PanicError { return &PanicError{recovered: recovered, stack: stack} } diff --git a/vendor/github.com/golangci/golangci-lint/internal/pkgcache/pkgcache.go b/vendor/github.com/golangci/golangci-lint/internal/pkgcache/pkgcache.go index f7eea210..3b3422eb 100644 --- a/vendor/github.com/golangci/golangci-lint/internal/pkgcache/pkgcache.go +++ b/vendor/github.com/golangci/golangci-lint/internal/pkgcache/pkgcache.go @@ -54,7 +54,7 @@ func (c *Cache) Trim() { }) } -func (c *Cache) Put(pkg *packages.Package, mode HashMode, key string, data interface{}) error { +func (c *Cache) Put(pkg *packages.Package, mode HashMode, key string, data any) error { var err error buf := &bytes.Buffer{} c.sw.TrackStage("gob", func() { @@ -93,7 +93,7 @@ func (c *Cache) Put(pkg *packages.Package, mode HashMode, key string, data inter var ErrMissing = errors.New("missing data") -func (c *Cache) Get(pkg *packages.Package, mode HashMode, key string, data interface{}) error { +func (c *Cache) Get(pkg *packages.Package, mode HashMode, key string, data any) error { var aID cache.ActionID var err error c.sw.TrackStage("key build", func() { diff --git a/vendor/github.com/golangci/golangci-lint/pkg/commands/run.go b/vendor/github.com/golangci/golangci-lint/pkg/commands/run.go index 20106b44..9149b177 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/commands/run.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/commands/run.go @@ -191,15 +191,6 @@ func initFlagSet(fs *pflag.FlagSet, cfg *config.Config, m *lintersdb.Manager, is true, "Goconst: ignore when constant is not used as function argument") hideFlag("goconst.ignore-calls") - // (@dixonwille) These flag is only used for testing purposes. - fs.StringSliceVar(&lsc.Depguard.Packages, "depguard.packages", nil, - "Depguard: packages to add to the list") - hideFlag("depguard.packages") - - fs.BoolVar(&lsc.Depguard.IncludeGoRoot, "depguard.include-go-root", false, - "Depguard: check list against standard lib") - hideFlag("depguard.include-go-root") - fs.IntVar(&lsc.Lll.TabWidth, "lll.tab-width", 1, "Lll: tab width in spaces") hideFlag("lll.tab-width") @@ -411,7 +402,7 @@ func (e *Executor) runAndPrint(ctx context.Context, args []string) error { out = append(out, "") } - err := e.printReports(ctx, issues, out[1], out[0]) + err := e.printReports(issues, out[1], out[0]) if err != nil { return err } @@ -424,7 +415,7 @@ func (e *Executor) runAndPrint(ctx context.Context, args []string) error { return nil } -func (e *Executor) printReports(ctx context.Context, issues []result.Issue, path, format string) error { +func (e *Executor) printReports(issues []result.Issue, path, format string) error { w, shouldClose, err := e.createWriter(path) if err != nil { return fmt.Errorf("can't create output for %s: %w", path, err) @@ -438,7 +429,7 @@ func (e *Executor) printReports(ctx context.Context, issues []result.Issue, path return err } - if err = p.Print(ctx, issues); err != nil { + if err = p.Print(issues); err != nil { if file, ok := w.(io.Closer); shouldClose && ok { _ = file.Close() } @@ -475,8 +466,10 @@ func (e *Executor) createPrinter(format string, w io.Writer) (printers.Printer, p = printers.NewText(e.cfg.Output.PrintIssuedLine, format == config.OutFormatColoredLineNumber, e.cfg.Output.PrintLinterName, e.log.Child(logutils.DebugKeyTextPrinter), w) - case config.OutFormatTab: - p = printers.NewTab(e.cfg.Output.PrintLinterName, e.log.Child(logutils.DebugKeyTabPrinter), w) + case config.OutFormatTab, config.OutFormatColoredTab: + p = printers.NewTab(e.cfg.Output.PrintLinterName, + format == config.OutFormatColoredTab, + e.log.Child(logutils.DebugKeyTabPrinter), w) case config.OutFormatCheckstyle: p = printers.NewCheckstyle(w) case config.OutFormatCodeClimate: diff --git a/vendor/github.com/golangci/golangci-lint/pkg/config/issues.go b/vendor/github.com/golangci/golangci-lint/pkg/config/issues.go index b2437ec9..417b28bd 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/config/issues.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/config/issues.go @@ -125,21 +125,25 @@ type ExcludeRule struct { BaseRule `mapstructure:",squash"` } -func (e ExcludeRule) Validate() error { +func (e *ExcludeRule) Validate() error { return e.BaseRule.Validate(excludeRuleMinConditionsCount) } type BaseRule struct { - Linters []string - Path string - Text string - Source string + Linters []string + Path string + PathExcept string `mapstructure:"path-except"` + Text string + Source string } -func (b BaseRule) Validate(minConditionsCount int) error { +func (b *BaseRule) Validate(minConditionsCount int) error { if err := validateOptionalRegex(b.Path); err != nil { return fmt.Errorf("invalid path regex: %v", err) } + if err := validateOptionalRegex(b.PathExcept); err != nil { + return fmt.Errorf("invalid path-except regex: %v", err) + } if err := validateOptionalRegex(b.Text); err != nil { return fmt.Errorf("invalid text regex: %v", err) } @@ -150,7 +154,10 @@ func (b BaseRule) Validate(minConditionsCount int) error { if len(b.Linters) > 0 { nonBlank++ } - if b.Path != "" { + // Filtering by path counts as one condition, regardless how it is done (one or both). + // Otherwise, a rule with Path and PathExcept set would pass validation + // whereas before the introduction of path-except that wouldn't have been precise enough. + if b.Path != "" || b.PathExcept != "" { nonBlank++ } if b.Text != "" { @@ -160,7 +167,7 @@ func (b BaseRule) Validate(minConditionsCount int) error { nonBlank++ } if nonBlank < minConditionsCount { - return fmt.Errorf("at least %d of (text, source, path, linters) should be set", minConditionsCount) + return fmt.Errorf("at least %d of (text, source, path[-except], linters) should be set", minConditionsCount) } return nil } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/config/linters_settings.go b/vendor/github.com/golangci/golangci-lint/pkg/config/linters_settings.go index a98b2b64..b520ea4c 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/config/linters_settings.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/config/linters_settings.go @@ -1,8 +1,11 @@ package config import ( + "encoding" "errors" "runtime" + + "gopkg.in/yaml.v3" ) var defaultLintersSettings = LintersSettings{ @@ -61,6 +64,12 @@ var defaultLintersSettings = LintersSettings{ Gosec: GoSecSettings{ Concurrency: runtime.NumCPU(), }, + Gosmopolitan: GosmopolitanSettings{ + AllowTimeLocal: false, + EscapeHatches: []string{}, + IgnoreTests: true, + WatchForScripts: []string{"Han"}, + }, Ifshort: IfshortSettings{ MaxDeclLines: 1, MaxDeclChars: 30, @@ -104,6 +113,11 @@ var defaultLintersSettings = LintersSettings{ Ignore: "", Qualified: false, }, + TagAlign: TagAlignSettings{ + Align: true, + Sort: true, + Order: nil, + }, Testpackage: TestpackageSettings{ SkipRegexp: `(export|internal)_test\.go`, AllowPackages: []string{"main"}, @@ -111,6 +125,10 @@ var defaultLintersSettings = LintersSettings{ Unparam: UnparamSettings{ Algo: "cha", }, + UseStdlibVars: UseStdlibVarsSettings{ + HTTPMethod: true, + HTTPStatusCode: true, + }, Varnamelen: VarnamelenSettings{ MaxDistance: 5, MinNameLength: 3, @@ -167,6 +185,7 @@ type LintersSettings struct { Gomodguard GoModGuardSettings Gosec GoSecSettings Gosimple StaticCheckSettings + Gosmopolitan GosmopolitanSettings Govet GovetSettings Grouper GrouperSettings Ifshort IfshortSettings @@ -196,6 +215,7 @@ type LintersSettings struct { Staticcheck StaticCheckSettings Structcheck StructCheckSettings Stylecheck StaticCheckSettings + TagAlign TagAlignSettings Tagliatelle TagliatelleSettings Tenv TenvSettings Testpackage TestpackageSettings @@ -236,12 +256,18 @@ type Cyclop struct { } type DepGuardSettings struct { - ListType string `mapstructure:"list-type"` - Packages []string - IncludeGoRoot bool `mapstructure:"include-go-root"` - PackagesWithErrorMessage map[string]string `mapstructure:"packages-with-error-message"` - IgnoreFileRules []string `mapstructure:"ignore-file-rules"` - AdditionalGuards []DepGuardSettings `mapstructure:"additional-guards"` + Rules map[string]*DepGuardList `mapstructure:"rules"` +} + +type DepGuardList struct { + Files []string `mapstructure:"files"` + Allow []string `mapstructure:"allow"` + Deny []DepGuardDeny `mapstructure:"deny"` +} + +type DepGuardDeny struct { + Pkg string `mapstructure:"pkg"` + Desc string `mapstructure:"desc"` } type DecorderSettings struct { @@ -307,8 +333,45 @@ type ExhaustructSettings struct { } type ForbidigoSettings struct { - Forbid []string `mapstructure:"forbid"` - ExcludeGodocExamples bool `mapstructure:"exclude-godoc-examples"` + Forbid []ForbidigoPattern `mapstructure:"forbid"` + ExcludeGodocExamples bool `mapstructure:"exclude-godoc-examples"` + AnalyzeTypes bool `mapstructure:"analyze-types"` +} + +var _ encoding.TextUnmarshaler = &ForbidigoPattern{} + +// ForbidigoPattern corresponds to forbidigo.pattern and adds mapstructure support. +// The YAML field names must match what forbidigo expects. +type ForbidigoPattern struct { + // patternString gets populated when the config contains a string as entry in ForbidigoSettings.Forbid[] + // because ForbidigoPattern implements encoding.TextUnmarshaler + // and the reader uses the mapstructure.TextUnmarshallerHookFunc as decoder hook. + // + // If the entry is a map, then the other fields are set as usual by mapstructure. + patternString string + + Pattern string `yaml:"p" mapstructure:"p"` + Package string `yaml:"pkg,omitempty" mapstructure:"pkg,omitempty"` + Msg string `yaml:"msg,omitempty" mapstructure:"msg,omitempty"` +} + +func (p *ForbidigoPattern) UnmarshalText(text []byte) error { + // Validation happens when instantiating forbidigo. + p.patternString = string(text) + return nil +} + +// MarshalString converts the pattern into a string as needed by forbidigo.NewLinter. +// +// MarshalString is intentionally not called MarshalText, +// although it has the same signature +// because implementing encoding.TextMarshaler led to infinite recursion when yaml.Marshal called MarshalText. +func (p *ForbidigoPattern) MarshalString() ([]byte, error) { + if p.patternString != "" { + return []byte(p.patternString), nil + } + + return yaml.Marshal(p) } type FunlenSettings struct { @@ -328,6 +391,7 @@ type GinkgoLinterSettings struct { SuppressNilAssertion bool `mapstructure:"suppress-nil-assertion"` SuppressErrAssertion bool `mapstructure:"suppress-err-assertion"` SuppressCompareAssertion bool `mapstructure:"suppress-compare-assertion"` + SuppressAsyncAssertion bool `mapstructure:"suppress-async-assertion"` AllowHaveLenZero bool `mapstructure:"allow-havelen-zero"` } @@ -347,6 +411,7 @@ type GoConstSettings struct { } type GoCriticSettings struct { + Go string `mapstructure:"-"` EnabledChecks []string `mapstructure:"enabled-checks"` DisabledChecks []string `mapstructure:"disabled-checks"` EnabledTags []string `mapstructure:"enabled-tags"` @@ -354,7 +419,7 @@ type GoCriticSettings struct { SettingsPerCheck map[string]GoCriticCheckSettings `mapstructure:"settings"` } -type GoCriticCheckSettings map[string]interface{} +type GoCriticCheckSettings map[string]any type GoCycloSettings struct { MinComplexity int `mapstructure:"min-complexity"` @@ -407,11 +472,11 @@ type GoLintSettings struct { } type GoMndSettings struct { - Settings map[string]map[string]interface{} // Deprecated - Checks []string `mapstructure:"checks"` - IgnoredNumbers []string `mapstructure:"ignored-numbers"` - IgnoredFiles []string `mapstructure:"ignored-files"` - IgnoredFunctions []string `mapstructure:"ignored-functions"` + Settings map[string]map[string]any // Deprecated + Checks []string `mapstructure:"checks"` + IgnoredNumbers []string `mapstructure:"ignored-numbers"` + IgnoredFiles []string `mapstructure:"ignored-files"` + IgnoredFunctions []string `mapstructure:"ignored-functions"` } type GoModDirectivesSettings struct { @@ -440,19 +505,26 @@ type GoModGuardSettings struct { } type GoSecSettings struct { - Includes []string `mapstructure:"includes"` - Excludes []string `mapstructure:"excludes"` - Severity string `mapstructure:"severity"` - Confidence string `mapstructure:"confidence"` - ExcludeGenerated bool `mapstructure:"exclude-generated"` - Config map[string]interface{} `mapstructure:"config"` - Concurrency int `mapstructure:"concurrency"` + Includes []string `mapstructure:"includes"` + Excludes []string `mapstructure:"excludes"` + Severity string `mapstructure:"severity"` + Confidence string `mapstructure:"confidence"` + ExcludeGenerated bool `mapstructure:"exclude-generated"` + Config map[string]any `mapstructure:"config"` + Concurrency int `mapstructure:"concurrency"` +} + +type GosmopolitanSettings struct { + AllowTimeLocal bool `mapstructure:"allow-time-local"` + EscapeHatches []string `mapstructure:"escape-hatches"` + IgnoreTests bool `mapstructure:"ignore-tests"` + WatchForScripts []string `mapstructure:"watch-for-scripts"` } type GovetSettings struct { Go string `mapstructure:"-"` CheckShadowing bool `mapstructure:"check-shadowing"` - Settings map[string]map[string]interface{} + Settings map[string]map[string]any Enable []string Disable []string @@ -607,7 +679,7 @@ type ReviveSettings struct { EnableAllRules bool `mapstructure:"enable-all-rules"` Rules []struct { Name string - Arguments []interface{} + Arguments []any Severity string Disabled bool } @@ -641,6 +713,12 @@ type StructCheckSettings struct { CheckExportedFields bool `mapstructure:"exported-fields"` } +type TagAlignSettings struct { + Align bool `mapstructure:"align"` + Sort bool `mapstructure:"sort"` + Order []string `mapstructure:"order"` +} + type TagliatelleSettings struct { Case struct { Rules map[string]string diff --git a/vendor/github.com/golangci/golangci-lint/pkg/config/output.go b/vendor/github.com/golangci/golangci-lint/pkg/config/output.go index 2c49ea7f..e8726392 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/config/output.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/config/output.go @@ -5,6 +5,7 @@ const ( OutFormatLineNumber = "line-number" OutFormatColoredLineNumber = "colored-line-number" OutFormatTab = "tab" + OutFormatColoredTab = "colored-tab" OutFormatCheckstyle = "checkstyle" OutFormatCodeClimate = "code-climate" OutFormatHTML = "html" @@ -28,11 +29,13 @@ var OutFormats = []string{ type Output struct { Format string - Color string PrintIssuedLine bool `mapstructure:"print-issued-lines"` PrintLinterName bool `mapstructure:"print-linter-name"` UniqByLine bool `mapstructure:"uniq-by-line"` SortResults bool `mapstructure:"sort-results"` PrintWelcomeMessage bool `mapstructure:"print-welcome"` PathPrefix string `mapstructure:"path-prefix"` + + // only work with CLI flags because the setup of logs is done before the config file parsing. + Color string } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/config/reader.go b/vendor/github.com/golangci/golangci-lint/pkg/config/reader.go index 2dfd3c06..de203876 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/config/reader.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/config/reader.go @@ -8,12 +8,13 @@ import ( "strings" "github.com/mitchellh/go-homedir" + "github.com/mitchellh/mapstructure" "github.com/spf13/viper" + "golang.org/x/exp/slices" "github.com/golangci/golangci-lint/pkg/exitcodes" "github.com/golangci/golangci-lint/pkg/fsutils" "github.com/golangci/golangci-lint/pkg/logutils" - "github.com/golangci/golangci-lint/pkg/sliceutil" ) type FileReader struct { @@ -72,18 +73,33 @@ func (r *FileReader) parseConfig() error { return nil } - usedConfigFile, err := fsutils.ShortestRelPath(usedConfigFile, "") - if err != nil { - r.log.Warnf("Can't pretty print config file path: %s", err) + if usedConfigFile == os.Stdin.Name() { + usedConfigFile = "" + r.log.Infof("Reading config file stdin") + } else { + var err error + usedConfigFile, err = fsutils.ShortestRelPath(usedConfigFile, "") + if err != nil { + r.log.Warnf("Can't pretty print config file path: %v", err) + } + + r.log.Infof("Used config file %s", usedConfigFile) } - r.log.Infof("Used config file %s", usedConfigFile) - usedConfigDir := filepath.Dir(usedConfigFile) - if usedConfigDir, err = filepath.Abs(usedConfigDir); err != nil { + + usedConfigDir, err := filepath.Abs(filepath.Dir(usedConfigFile)) + if err != nil { return errors.New("can't get config directory") } r.cfg.cfgDir = usedConfigDir - if err := viper.Unmarshal(r.cfg); err != nil { + if err := viper.Unmarshal(r.cfg, viper.DecodeHook(mapstructure.ComposeDecodeHookFunc( + // Default hooks (https://github.com/spf13/viper/blob/518241257478c557633ab36e474dfcaeb9a3c623/viper.go#L135-L138). + mapstructure.StringToTimeDurationHookFunc(), + mapstructure.StringToSliceHookFunc(","), + + // Needed for forbidigo. + mapstructure.TextUnmarshallerHookFunc(), + ))); err != nil { return fmt.Errorf("can't unmarshal config by viper: %s", err) } @@ -195,7 +211,7 @@ func (r *FileReader) setupConfigFileSearch() { // find home directory for global config if home, err := homedir.Dir(); err != nil { r.log.Warnf("Can't get user's home directory: %s", err.Error()) - } else if !sliceutil.Contains(configSearchPaths, home) { + } else if !slices.Contains(configSearchPaths, home) { configSearchPaths = append(configSearchPaths, home) } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/fsutils/filecache.go b/vendor/github.com/golangci/golangci-lint/pkg/fsutils/filecache.go index 58dcdf0e..e8e5ba19 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/fsutils/filecache.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/fsutils/filecache.go @@ -54,7 +54,7 @@ func PrettifyBytesCount(n int64) string { func (fc *FileCache) PrintStats(log logutils.Log) { var size int64 var mapLen int - fc.files.Range(func(_, fileBytes interface{}) bool { + fc.files.Range(func(_, fileBytes any) bool { mapLen++ size += int64(len(fileBytes.([]byte))) diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/bidichk.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/bidichk.go index 44215b7e..e1b34717 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/bidichk.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/bidichk.go @@ -13,7 +13,7 @@ import ( func NewBiDiChkFuncName(cfg *config.BiDiChkSettings) *goanalysis.Linter { a := bidichk.NewAnalyzer() - cfgMap := map[string]map[string]interface{}{} + cfgMap := map[string]map[string]any{} if cfg != nil { var opts []string @@ -45,7 +45,7 @@ func NewBiDiChkFuncName(cfg *config.BiDiChkSettings) *goanalysis.Linter { opts = append(opts, "POP-DIRECTIONAL-ISOLATE") } - cfgMap[a.Name] = map[string]interface{}{ + cfgMap[a.Name] = map[string]any{ "disallowed-runes": strings.Join(opts, ","), } } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/containedctx.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/containedctx.go index 8592eef1..8f7859af 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/containedctx.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/containedctx.go @@ -15,5 +15,5 @@ func NewContainedCtx() *goanalysis.Linter { a.Doc, []*analysis.Analyzer{a}, nil, - ).WithLoadMode(goanalysis.LoadModeSyntax) + ).WithLoadMode(goanalysis.LoadModeTypesInfo) } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/cyclop.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/cyclop.go index 6f55b279..5ad65f12 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/cyclop.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/cyclop.go @@ -13,9 +13,9 @@ const cyclopName = "cyclop" func NewCyclop(settings *config.Cyclop) *goanalysis.Linter { a := analyzer.NewAnalyzer() - var cfg map[string]map[string]interface{} + var cfg map[string]map[string]any if settings != nil { - d := map[string]interface{}{ + d := map[string]any{ "skipTests": settings.SkipTests, } @@ -27,7 +27,7 @@ func NewCyclop(settings *config.Cyclop) *goanalysis.Linter { d["packageAverage"] = settings.PackageAverage } - cfg = map[string]map[string]interface{}{a.Name: d} + cfg = map[string]map[string]any{a.Name: d} } return goanalysis.NewLinter( diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/deadcode.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/deadcode.go index 408b180b..4f563c38 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/deadcode.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/deadcode.go @@ -21,7 +21,7 @@ func NewDeadcode() *goanalysis.Linter { analyzer := &analysis.Analyzer{ Name: deadcodeName, Doc: goanalysis.TheOnlyanalyzerDoc, - Run: func(pass *analysis.Pass) (interface{}, error) { + Run: func(pass *analysis.Pass) (any, error) { prog := goanalysis.MakeFakeLoaderProgram(pass) issues, err := deadcodeAPI.Run(prog) diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/decorder.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/decorder.go index 1c93acaa..9d492c4e 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/decorder.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/decorder.go @@ -14,7 +14,7 @@ func NewDecorder(settings *config.DecorderSettings) *goanalysis.Linter { a := decorder.Analyzer // disable all rules/checks by default - cfg := map[string]interface{}{ + cfg := map[string]any{ "disable-dec-num-check": true, "disable-dec-order-check": true, "disable-init-func-first-check": true, @@ -31,6 +31,6 @@ func NewDecorder(settings *config.DecorderSettings) *goanalysis.Linter { a.Name, a.Doc, []*analysis.Analyzer{a}, - map[string]map[string]interface{}{a.Name: cfg}, + map[string]map[string]any{a.Name: cfg}, ).WithLoadMode(goanalysis.LoadModeSyntax) } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/depguard.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/depguard.go index eb7b0f33..db0df7b4 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/depguard.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/depguard.go @@ -1,200 +1,46 @@ package golinters import ( - "fmt" - "strings" - "sync" - - "github.com/OpenPeeDeeP/depguard" + "github.com/OpenPeeDeeP/depguard/v2" "golang.org/x/tools/go/analysis" - "golang.org/x/tools/go/loader" //nolint:staticcheck // require changes in github.com/OpenPeeDeeP/depguard "github.com/golangci/golangci-lint/pkg/config" - "github.com/golangci/golangci-lint/pkg/fsutils" "github.com/golangci/golangci-lint/pkg/golinters/goanalysis" - "github.com/golangci/golangci-lint/pkg/lint/linter" - "github.com/golangci/golangci-lint/pkg/result" ) -const depguardName = "depguard" - func NewDepguard(settings *config.DepGuardSettings) *goanalysis.Linter { - var mu sync.Mutex - var resIssues []goanalysis.Issue - - analyzer := &analysis.Analyzer{ - Name: depguardName, - Doc: goanalysis.TheOnlyanalyzerDoc, - Run: goanalysis.DummyRun, - } - - return goanalysis.NewLinter( - depguardName, - "Go linter that checks if package imports are in a list of acceptable packages", - []*analysis.Analyzer{analyzer}, - nil, - ).WithContextSetter(func(lintCtx *linter.Context) { - dg, err := newDepGuard(settings) - - analyzer.Run = func(pass *analysis.Pass) (interface{}, error) { - if err != nil { - return nil, err - } + conf := depguard.LinterSettings{} - issues, errRun := dg.run(pass) - if errRun != nil { - return nil, errRun + if settings != nil { + for s, rule := range settings.Rules { + list := &depguard.List{ + Files: rule.Files, + Allow: rule.Allow, } - mu.Lock() - resIssues = append(resIssues, issues...) - mu.Unlock() - - return nil, nil - } - }).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue { - return resIssues - }).WithLoadMode(goanalysis.LoadModeSyntax) -} - -type depGuard struct { - loadConfig *loader.Config - guardians []*guardian -} - -func newDepGuard(settings *config.DepGuardSettings) (*depGuard, error) { - ps, err := newGuardian(settings) - if err != nil { - return nil, err - } - - d := &depGuard{ - loadConfig: &loader.Config{ - Cwd: "", // fallbacked to os.Getcwd - Build: nil, // fallbacked to build.Default - }, - guardians: []*guardian{ps}, - } - - for _, additional := range settings.AdditionalGuards { - add := additional - ps, err = newGuardian(&add) - if err != nil { - return nil, err - } - - d.guardians = append(d.guardians, ps) - } - - return d, nil -} - -func (d depGuard) run(pass *analysis.Pass) ([]goanalysis.Issue, error) { - prog := goanalysis.MakeFakeLoaderProgram(pass) - - var resIssues []goanalysis.Issue - for _, g := range d.guardians { - issues, errRun := g.run(d.loadConfig, prog, pass) - if errRun != nil { - return nil, errRun - } - - resIssues = append(resIssues, issues...) - } - - return resIssues, nil -} - -type guardian struct { - *depguard.Depguard - pkgsWithErrorMessage map[string]string -} - -func newGuardian(settings *config.DepGuardSettings) (*guardian, error) { - var ignoreFileRules []string - for _, rule := range settings.IgnoreFileRules { - ignoreFileRules = append(ignoreFileRules, fsutils.NormalizePathInRegex(rule)) - } - - dg := &depguard.Depguard{ - Packages: settings.Packages, - IncludeGoRoot: settings.IncludeGoRoot, - IgnoreFileRules: ignoreFileRules, - } - - var err error - dg.ListType, err = getDepGuardListType(settings.ListType) - if err != nil { - return nil, err - } - - // if the list type was a denylist the packages with error messages should be included in the denylist package list - if dg.ListType == depguard.LTBlacklist { - noMessagePackages := make(map[string]bool) - for _, pkg := range dg.Packages { - noMessagePackages[pkg] = true - } + // because of bug with Viper parsing (split on dot) we use a list of struct instead of a map. + // https://github.com/spf13/viper/issues/324 + // https://github.com/golangci/golangci-lint/issues/3749#issuecomment-1492536630 - for pkg := range settings.PackagesWithErrorMessage { - if _, ok := noMessagePackages[pkg]; !ok { - dg.Packages = append(dg.Packages, pkg) + deny := map[string]string{} + for _, r := range rule.Deny { + deny[r.Pkg] = r.Desc } - } - } - - return &guardian{ - Depguard: dg, - pkgsWithErrorMessage: settings.PackagesWithErrorMessage, - }, nil -} - -func (g guardian) run(loadConfig *loader.Config, prog *loader.Program, pass *analysis.Pass) ([]goanalysis.Issue, error) { - issues, err := g.Run(loadConfig, prog) - if err != nil { - return nil, err - } - - res := make([]goanalysis.Issue, 0, len(issues)) - - for _, issue := range issues { - res = append(res, - goanalysis.NewIssue(&result.Issue{ - Pos: issue.Position, - Text: g.createMsg(issue.PackageName), - FromLinter: depguardName, - }, pass), - ) - } - - return res, nil -} - -func (g guardian) createMsg(pkgName string) string { - msgSuffix := "is in the denylist" - if g.ListType == depguard.LTWhitelist { - msgSuffix = "is not in the allowlist" - } + list.Deny = deny - var userSuppliedMsgSuffix string - if g.pkgsWithErrorMessage != nil { - userSuppliedMsgSuffix = g.pkgsWithErrorMessage[pkgName] - if userSuppliedMsgSuffix != "" { - userSuppliedMsgSuffix = ": " + userSuppliedMsgSuffix + conf[s] = list } } - return fmt.Sprintf("%s %s%s", formatCode(pkgName, nil), msgSuffix, userSuppliedMsgSuffix) -} - -func getDepGuardListType(listType string) (depguard.ListType, error) { - if listType == "" { - return depguard.LTBlacklist, nil - } - - listT, found := depguard.StringToListType[strings.ToLower(listType)] - if !found { - return depguard.LTBlacklist, fmt.Errorf("unsure what list type %s is", listType) + a, err := depguard.NewAnalyzer(&conf) + if err != nil { + linterLogger.Fatalf("depguard: create analyzer: %v", err) } - return listT, nil + return goanalysis.NewLinter( + a.Name, + a.Doc, + []*analysis.Analyzer{a}, + nil, + ).WithLoadMode(goanalysis.LoadModeSyntax) } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/dogsled.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/dogsled.go index 00c32c2d..79502fe8 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/dogsled.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/dogsled.go @@ -24,7 +24,7 @@ func NewDogsled(settings *config.DogsledSettings) *goanalysis.Linter { analyzer := &analysis.Analyzer{ Name: dogsledName, Doc: goanalysis.TheOnlyanalyzerDoc, - Run: func(pass *analysis.Pass) (interface{}, error) { + Run: func(pass *analysis.Pass) (any, error) { issues := runDogsled(pass, settings) if len(issues) == 0 { diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/dupl.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/dupl.go index fd04d9a9..5d772a5f 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/dupl.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/dupl.go @@ -25,7 +25,7 @@ func NewDupl(settings *config.DuplSettings) *goanalysis.Linter { analyzer := &analysis.Analyzer{ Name: duplName, Doc: goanalysis.TheOnlyanalyzerDoc, - Run: func(pass *analysis.Pass) (interface{}, error) { + Run: func(pass *analysis.Pass) (any, error) { issues, err := runDupl(pass, settings) if err != nil { return nil, err diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/dupword.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/dupword.go index ae85a6d0..f5a99bc0 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/dupword.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/dupword.go @@ -13,9 +13,9 @@ import ( func NewDupWord(setting *config.DupWordSettings) *goanalysis.Linter { a := dupword.NewAnalyzer() - cfgMap := map[string]map[string]interface{}{} + cfgMap := map[string]map[string]any{} if setting != nil { - cfgMap[a.Name] = map[string]interface{}{ + cfgMap[a.Name] = map[string]any{ "keyword": strings.Join(setting.Keywords, ","), } } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/errcheck.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/errcheck.go index d031727a..89b18519 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/errcheck.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/errcheck.go @@ -49,7 +49,7 @@ func NewErrcheck(settings *config.ErrcheckSettings) *goanalysis.Linter { checker.Tags = lintCtx.Cfg.Run.BuildTags - analyzer.Run = func(pass *analysis.Pass) (interface{}, error) { + analyzer.Run = func(pass *analysis.Pass) (any, error) { issues := runErrCheck(lintCtx, pass, checker) if err != nil { return nil, err diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/errchkjson.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/errchkjson.go index 6cc2208a..171de00a 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/errchkjson.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/errchkjson.go @@ -11,12 +11,12 @@ import ( func NewErrChkJSONFuncName(cfg *config.ErrChkJSONSettings) *goanalysis.Linter { a := errchkjson.NewAnalyzer() - cfgMap := map[string]map[string]interface{}{} - cfgMap[a.Name] = map[string]interface{}{ + cfgMap := map[string]map[string]any{} + cfgMap[a.Name] = map[string]any{ "omit-safe": true, } if cfg != nil { - cfgMap[a.Name] = map[string]interface{}{ + cfgMap[a.Name] = map[string]any{ "omit-safe": !cfg.CheckErrorFreeEncoding, "report-no-exported": cfg.ReportNoExported, } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/errorlint.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/errorlint.go index 36c976a5..cac94159 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/errorlint.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/errorlint.go @@ -11,10 +11,10 @@ import ( func NewErrorLint(cfg *config.ErrorLintSettings) *goanalysis.Linter { a := errorlint.NewAnalyzer() - cfgMap := map[string]map[string]interface{}{} + cfgMap := map[string]map[string]any{} if cfg != nil { - cfgMap[a.Name] = map[string]interface{}{ + cfgMap[a.Name] = map[string]any{ "errorf": cfg.Errorf, "errorf-multi": cfg.ErrorfMulti, "asserts": cfg.Asserts, diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/exhaustive.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/exhaustive.go index a66f9519..3824afa0 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/exhaustive.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/exhaustive.go @@ -11,9 +11,9 @@ import ( func NewExhaustive(settings *config.ExhaustiveSettings) *goanalysis.Linter { a := exhaustive.Analyzer - var cfg map[string]map[string]interface{} + var cfg map[string]map[string]any if settings != nil { - cfg = map[string]map[string]interface{}{ + cfg = map[string]map[string]any{ a.Name: { exhaustive.CheckFlag: settings.Check, exhaustive.CheckGeneratedFlag: settings.CheckGenerated, diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/exhaustivestruct.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/exhaustivestruct.go index 6a1dbd71..9bc9bbfb 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/exhaustivestruct.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/exhaustivestruct.go @@ -13,9 +13,9 @@ import ( func NewExhaustiveStruct(settings *config.ExhaustiveStructSettings) *goanalysis.Linter { a := analyzer.Analyzer - var cfg map[string]map[string]interface{} + var cfg map[string]map[string]any if settings != nil { - cfg = map[string]map[string]interface{}{ + cfg = map[string]map[string]any{ a.Name: { "struct_patterns": strings.Join(settings.StructPatterns, ","), }, diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/forbidigo.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/forbidigo.go index 4879cca0..6aced292 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/forbidigo.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/forbidigo.go @@ -10,6 +10,7 @@ import ( "github.com/golangci/golangci-lint/pkg/config" "github.com/golangci/golangci-lint/pkg/golinters/goanalysis" "github.com/golangci/golangci-lint/pkg/lint/linter" + "github.com/golangci/golangci-lint/pkg/logutils" "github.com/golangci/golangci-lint/pkg/result" ) @@ -23,7 +24,7 @@ func NewForbidigo(settings *config.ForbidigoSettings) *goanalysis.Linter { analyzer := &analysis.Analyzer{ Name: forbidigoName, Doc: goanalysis.TheOnlyanalyzerDoc, - Run: func(pass *analysis.Pass) (interface{}, error) { + Run: func(pass *analysis.Pass) (any, error) { issues, err := runForbidigo(pass, settings) if err != nil { return nil, err @@ -40,6 +41,9 @@ func NewForbidigo(settings *config.ForbidigoSettings) *goanalysis.Linter { }, } + // Without AnalyzeTypes, LoadModeSyntax is enough. + // But we cannot make this depend on the settings and have to mirror the mode chosen in GetAllSupportedLinterConfigs, + // therefore we have to use LoadModeTypesInfo in all cases. return goanalysis.NewLinter( forbidigoName, "Forbids identifiers", @@ -55,16 +59,34 @@ func runForbidigo(pass *analysis.Pass, settings *config.ForbidigoSettings) ([]go forbidigo.OptionExcludeGodocExamples(settings.ExcludeGodocExamples), // disable "//permit" directives so only "//nolint" directives matters within golangci-lint forbidigo.OptionIgnorePermitDirectives(true), + forbidigo.OptionAnalyzeTypes(settings.AnalyzeTypes), } - forbid, err := forbidigo.NewLinter(settings.Forbid, options...) + // Convert patterns back to strings because that is what NewLinter accepts. + var patterns []string + for _, pattern := range settings.Forbid { + buffer, err := pattern.MarshalString() + if err != nil { + return nil, err + } + patterns = append(patterns, string(buffer)) + } + + forbid, err := forbidigo.NewLinter(patterns, options...) if err != nil { return nil, fmt.Errorf("failed to create linter %q: %w", forbidigoName, err) } var issues []goanalysis.Issue for _, file := range pass.Files { - hints, err := forbid.RunWithConfig(forbidigo.RunConfig{Fset: pass.Fset}, file) + runConfig := forbidigo.RunConfig{ + Fset: pass.Fset, + DebugLog: logutils.Debug(logutils.DebugKeyForbidigo), + } + if settings != nil && settings.AnalyzeTypes { + runConfig.TypesInfo = pass.TypesInfo + } + hints, err := forbid.RunWithConfig(runConfig, file) if err != nil { return nil, fmt.Errorf("forbidigo linter failed on file %q: %w", file.Name.String(), err) } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/funlen.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/funlen.go index c562c2aa..aae1623c 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/funlen.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/funlen.go @@ -24,7 +24,7 @@ func NewFunlen(settings *config.FunlenSettings) *goanalysis.Linter { analyzer := &analysis.Analyzer{ Name: funlenName, Doc: goanalysis.TheOnlyanalyzerDoc, - Run: func(pass *analysis.Pass) (interface{}, error) { + Run: func(pass *analysis.Pass) (any, error) { issues := runFunlen(pass, settings) if len(issues) == 0 { diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/gci.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/gci.go index 92c5fc61..4eb26dbd 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/gci.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/gci.go @@ -2,7 +2,6 @@ package golinters import ( "fmt" - "strings" "sync" gcicfg "github.com/daixiang0/gci/pkg/config" @@ -61,7 +60,7 @@ func NewGci(settings *config.GciSettings) *goanalysis.Linter { []*analysis.Analyzer{analyzer}, nil, ).WithContextSetter(func(lintCtx *linter.Context) { - analyzer.Run = func(pass *analysis.Pass) (interface{}, error) { + analyzer.Run = func(pass *analysis.Pass) (any, error) { issues, err := runGci(pass, lintCtx, cfg, &lock) if err != nil { return nil, err @@ -145,7 +144,13 @@ func getErrorTextForGci(settings config.GciSettings) string { } if len(settings.Sections) > 0 { - text += " -s " + strings.Join(settings.Sections, ",") + for _, section := range settings.Sections { + text += " -s " + section + } + } + + if settings.CustomOrder { + text += " --custom-order" } return text diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/ginkgolinter.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/ginkgolinter.go index ea2f4752..b9e69b26 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/ginkgolinter.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/ginkgolinter.go @@ -11,20 +11,21 @@ import ( func NewGinkgoLinter(cfg *config.GinkgoLinterSettings) *goanalysis.Linter { a := ginkgolinter.NewAnalyzer() - cfgMap := make(map[string]map[string]interface{}) + cfgMap := make(map[string]map[string]any) if cfg != nil { - cfgMap[a.Name] = map[string]interface{}{ + cfgMap[a.Name] = map[string]any{ "suppress-len-assertion": cfg.SuppressLenAssertion, "suppress-nil-assertion": cfg.SuppressNilAssertion, "suppress-err-assertion": cfg.SuppressErrAssertion, "suppress-compare-assertion": cfg.SuppressCompareAssertion, + "suppress-async-assertion": cfg.SuppressAsyncAssertion, "allow-havelen-0": cfg.AllowHaveLenZero, } } return goanalysis.NewLinter( a.Name, - a.Doc, + "enforces standards of using ginkgo and gomega", []*analysis.Analyzer{a}, cfgMap, ).WithLoadMode(goanalysis.LoadModeTypesInfo) diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/goanalysis/linter.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/goanalysis/linter.go index 66944867..f8ca2e75 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/goanalysis/linter.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/goanalysis/linter.go @@ -44,14 +44,14 @@ const ( type Linter struct { name, desc string analyzers []*analysis.Analyzer - cfg map[string]map[string]interface{} + cfg map[string]map[string]any issuesReporter func(*linter.Context) []Issue contextSetter func(*linter.Context) loadMode LoadMode needUseOriginalPackages bool } -func NewLinter(name, desc string, analyzers []*analysis.Analyzer, cfg map[string]map[string]interface{}) *Linter { +func NewLinter(name, desc string, analyzers []*analysis.Analyzer, cfg map[string]map[string]any) *Linter { return &Linter{name: name, desc: desc, analyzers: analyzers, cfg: cfg} } @@ -102,7 +102,7 @@ func (lnt *Linter) allAnalyzerNames() []string { return ret } -func (lnt *Linter) configureAnalyzer(a *analysis.Analyzer, cfg map[string]interface{}) error { +func (lnt *Linter) configureAnalyzer(a *analysis.Analyzer, cfg map[string]any) error { for k, v := range cfg { f := a.Flags.Lookup(k) if f == nil { @@ -195,12 +195,12 @@ func allFlagNames(fs *flag.FlagSet) []string { return ret } -func valueToString(v interface{}) string { +func valueToString(v any) string { if ss, ok := v.([]string); ok { return strings.Join(ss, ",") } - if is, ok := v.([]interface{}); ok { + if is, ok := v.([]any); ok { var ss []string for _, i := range is { ss = append(ss, fmt.Sprint(i)) @@ -212,6 +212,6 @@ func valueToString(v interface{}) string { return fmt.Sprint(v) } -func DummyRun(_ *analysis.Pass) (interface{}, error) { +func DummyRun(_ *analysis.Pass) (any, error) { return nil, nil } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/goanalysis/runner_action.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/goanalysis/runner_action.go index a9d50c4c..5ded9fac 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/goanalysis/runner_action.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/goanalysis/runner_action.go @@ -50,7 +50,7 @@ type action struct { deps []*action objectFacts map[objectFactKey]analysis.Fact packageFacts map[packageFactKey]analysis.Fact - result interface{} + result any diagnostics []analysis.Diagnostic err error r *runner @@ -145,7 +145,7 @@ func (act *action) analyze() { // Plumb the output values of the dependencies // into the inputs of this action. Also facts. - inputs := make(map[*analysis.Analyzer]interface{}) + inputs := make(map[*analysis.Analyzer]any) startedAt := time.Now() for _, dep := range act.deps { if dep.pkg == act.pkg { diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/goanalysis/runner_loadingpackage.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/goanalysis/runner_loadingpackage.go index f16bf892..e39e2212 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/goanalysis/runner_loadingpackage.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/goanalysis/runner_loadingpackage.go @@ -446,7 +446,7 @@ type importerFunc func(path string) (*types.Package, error) func (f importerFunc) Import(path string) (*types.Package, error) { return f(path) } -func sizeOfValueTreeBytes(v interface{}) int { +func sizeOfValueTreeBytes(v any) int { return sizeOfReflectValueTreeBytes(reflect.ValueOf(v), map[uintptr]struct{}{}) } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/gochecknoglobals.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/gochecknoglobals.go index 0fe67e2b..6e18aeb2 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/gochecknoglobals.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/gochecknoglobals.go @@ -14,7 +14,7 @@ func NewGochecknoglobals() *goanalysis.Linter { // pass the `t` flag as true to the analyzer before running it. This can be // turned off by using the regular golangci-lint flags such as `--tests` or // `--skip-files`. - linterConfig := map[string]map[string]interface{}{ + linterConfig := map[string]map[string]any{ gochecknoglobals.Name: { "t": true, }, diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/gochecknoinits.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/gochecknoinits.go index bb0b783c..a51b531b 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/gochecknoinits.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/gochecknoinits.go @@ -22,7 +22,7 @@ func NewGochecknoinits() *goanalysis.Linter { analyzer := &analysis.Analyzer{ Name: gochecknoinitsName, Doc: goanalysis.TheOnlyanalyzerDoc, - Run: func(pass *analysis.Pass) (interface{}, error) { + Run: func(pass *analysis.Pass) (any, error) { var res []goanalysis.Issue for _, file := range pass.Files { fileIssues := checkFileForInits(file, pass.Fset) diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/gocognit.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/gocognit.go index 49146c52..406d34ed 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/gocognit.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/gocognit.go @@ -24,7 +24,7 @@ func NewGocognit(settings *config.GocognitSettings) *goanalysis.Linter { analyzer := &analysis.Analyzer{ Name: goanalysis.TheOnlyAnalyzerName, Doc: goanalysis.TheOnlyanalyzerDoc, - Run: func(pass *analysis.Pass) (interface{}, error) { + Run: func(pass *analysis.Pass) (any, error) { issues := runGocognit(pass, settings) if len(issues) == 0 { diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/goconst.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/goconst.go index 24d3198b..e277509d 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/goconst.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/goconst.go @@ -23,7 +23,7 @@ func NewGoconst(settings *config.GoConstSettings) *goanalysis.Linter { analyzer := &analysis.Analyzer{ Name: goconstName, Doc: goanalysis.TheOnlyanalyzerDoc, - Run: func(pass *analysis.Pass) (interface{}, error) { + Run: func(pass *analysis.Pass) (any, error) { issues, err := runGoconst(pass, settings) if err != nil { return nil, err diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/gocritic.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/gocritic.go index 2e241b5a..1319c72d 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/gocritic.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/gocritic.go @@ -42,7 +42,7 @@ func NewGoCritic(settings *config.GoCriticSettings, cfg *config.Config) *goanaly analyzer := &analysis.Analyzer{ Name: goCriticName, Doc: goanalysis.TheOnlyanalyzerDoc, - Run: func(pass *analysis.Pass) (interface{}, error) { + Run: func(pass *analysis.Pass) (any, error) { issues, err := wrapper.run(pass) if err != nil { return nil, err @@ -113,6 +113,8 @@ func (w *goCriticWrapper) run(pass *analysis.Pass) ([]goanalysis.Issue, error) { linterCtx := gocriticlinter.NewContext(pass.Fset, w.sizes) + linterCtx.SetGoVersion(w.settingsWrapper.Go) + enabledCheckers, err := w.buildEnabledCheckers(linterCtx) if err != nil { return nil, err @@ -245,7 +247,7 @@ func normalizeCheckerInfoParams(info *gocriticlinter.CheckerInfo) gocriticlinter // but the file parsers (TOML, YAML, JSON) don't create the same representation for raw type. // then we have to convert value types into the expected value types. // Maybe in the future, this kind of conversion will be done in go-critic itself. -func (w *goCriticWrapper) normalizeCheckerParamsValue(p interface{}) interface{} { +func (w *goCriticWrapper) normalizeCheckerParamsValue(p any) any { rv := reflect.ValueOf(p) switch rv.Type().Kind() { case reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8, reflect.Int: @@ -621,7 +623,7 @@ func sprintStrings(ss []string) string { return fmt.Sprint(ss) } -func debugChecksListf(checks []string, format string, args ...interface{}) { +func debugChecksListf(checks []string, format string, args ...any) { if !isGoCriticDebug { return } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/gocyclo.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/gocyclo.go index ea821957..b502623b 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/gocyclo.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/gocyclo.go @@ -23,7 +23,7 @@ func NewGocyclo(settings *config.GoCycloSettings) *goanalysis.Linter { analyzer := &analysis.Analyzer{ Name: gocycloName, Doc: goanalysis.TheOnlyanalyzerDoc, - Run: func(pass *analysis.Pass) (interface{}, error) { + Run: func(pass *analysis.Pass) (any, error) { issues := runGoCyclo(pass, settings) if len(issues) == 0 { diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/godot.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/godot.go index 93ca7577..b0ee6443 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/godot.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/godot.go @@ -42,7 +42,7 @@ func NewGodot(settings *config.GodotSettings) *goanalysis.Linter { analyzer := &analysis.Analyzer{ Name: godotName, Doc: goanalysis.TheOnlyanalyzerDoc, - Run: func(pass *analysis.Pass) (interface{}, error) { + Run: func(pass *analysis.Pass) (any, error) { issues, err := runGodot(pass, dotSettings) if err != nil { return nil, err diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/godox.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/godox.go index 4dba9df0..95581041 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/godox.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/godox.go @@ -24,7 +24,7 @@ func NewGodox(settings *config.GodoxSettings) *goanalysis.Linter { analyzer := &analysis.Analyzer{ Name: godoxName, Doc: goanalysis.TheOnlyanalyzerDoc, - Run: func(pass *analysis.Pass) (interface{}, error) { + Run: func(pass *analysis.Pass) (any, error) { issues := runGodox(pass, settings) if len(issues) == 0 { diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/gofmt.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/gofmt.go index 6736e569..d2d0d3cc 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/gofmt.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/gofmt.go @@ -31,7 +31,7 @@ func NewGofmt(settings *config.GoFmtSettings) *goanalysis.Linter { []*analysis.Analyzer{analyzer}, nil, ).WithContextSetter(func(lintCtx *linter.Context) { - analyzer.Run = func(pass *analysis.Pass) (interface{}, error) { + analyzer.Run = func(pass *analysis.Pass) (any, error) { issues, err := runGofmt(lintCtx, pass, settings) if err != nil { return nil, err diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/gofumpt.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/gofumpt.go index 276dab5f..c2aaf121 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/gofumpt.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/gofumpt.go @@ -50,7 +50,7 @@ func NewGofumpt(settings *config.GofumptSettings) *goanalysis.Linter { []*analysis.Analyzer{analyzer}, nil, ).WithContextSetter(func(lintCtx *linter.Context) { - analyzer.Run = func(pass *analysis.Pass) (interface{}, error) { + analyzer.Run = func(pass *analysis.Pass) (any, error) { issues, err := runGofumpt(lintCtx, pass, diff, options) if err != nil { return nil, err diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/goheader.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/goheader.go index d7d27326..d3cfefa9 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/goheader.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/goheader.go @@ -31,7 +31,7 @@ func NewGoHeader(settings *config.GoHeaderSettings) *goanalysis.Linter { analyzer := &analysis.Analyzer{ Name: goHeaderName, Doc: goanalysis.TheOnlyanalyzerDoc, - Run: func(pass *analysis.Pass) (interface{}, error) { + Run: func(pass *analysis.Pass) (any, error) { issues, err := runGoHeader(pass, conf) if err != nil { return nil, err diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/goimports.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/goimports.go index 00732857..aac27f38 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/goimports.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/goimports.go @@ -34,7 +34,7 @@ func NewGoimports(settings *config.GoImportsSettings) *goanalysis.Linter { ).WithContextSetter(func(lintCtx *linter.Context) { imports.LocalPrefix = settings.LocalPrefixes - analyzer.Run = func(pass *analysis.Pass) (interface{}, error) { + analyzer.Run = func(pass *analysis.Pass) (any, error) { issues, err := runGoImports(lintCtx, pass) if err != nil { return nil, err diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/golint.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/golint.go index 95c579e3..a6fc73c9 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/golint.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/golint.go @@ -23,7 +23,7 @@ func NewGolint(settings *config.GoLintSettings) *goanalysis.Linter { analyzer := &analysis.Analyzer{ Name: golintName, Doc: goanalysis.TheOnlyanalyzerDoc, - Run: func(pass *analysis.Pass) (interface{}, error) { + Run: func(pass *analysis.Pass) (any, error) { issues, err := runGoLint(pass, settings) if err != nil { return nil, err diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/gomnd.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/gomnd.go index 15d84b48..2e6d77a8 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/gomnd.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/gomnd.go @@ -9,14 +9,14 @@ import ( ) func NewGoMND(settings *config.GoMndSettings) *goanalysis.Linter { - var linterCfg map[string]map[string]interface{} + var linterCfg map[string]map[string]any if settings != nil { // TODO(ldez) For compatibility only, must be drop in v2. if len(settings.Settings) > 0 { linterCfg = settings.Settings } else { - cfg := make(map[string]interface{}) + cfg := make(map[string]any) if len(settings.Checks) > 0 { cfg["checks"] = settings.Checks } @@ -30,7 +30,7 @@ func NewGoMND(settings *config.GoMndSettings) *goanalysis.Linter { cfg["ignored-functions"] = settings.IgnoredFunctions } - linterCfg = map[string]map[string]interface{}{ + linterCfg = map[string]map[string]any{ "mnd": cfg, } } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/gomoddirectives.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/gomoddirectives.go index 81831129..56afcd46 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/gomoddirectives.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/gomoddirectives.go @@ -39,7 +39,7 @@ func NewGoModDirectives(settings *config.GoModDirectivesSettings) *goanalysis.Li []*analysis.Analyzer{analyzer}, nil, ).WithContextSetter(func(lintCtx *linter.Context) { - analyzer.Run = func(pass *analysis.Pass) (interface{}, error) { + analyzer.Run = func(pass *analysis.Pass) (any, error) { once.Do(func() { results, err := gomoddirectives.Analyze(opts) if err != nil { diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/gomodguard.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/gomodguard.go index e21658d5..157bf56c 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/gomodguard.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/gomodguard.go @@ -72,7 +72,7 @@ func NewGomodguard(settings *config.GoModGuardSettings) *goanalysis.Linter { return } - analyzer.Run = func(pass *analysis.Pass) (interface{}, error) { + analyzer.Run = func(pass *analysis.Pass) (any, error) { gomodguardIssues := processor.ProcessFiles(getFileNames(pass)) mu.Lock() diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/gosec.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/gosec.go index 4866bb38..235f0e91 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/gosec.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/gosec.go @@ -10,6 +10,7 @@ import ( "sync" "github.com/securego/gosec/v2" + "github.com/securego/gosec/v2/issue" "github.com/securego/gosec/v2/rules" "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/packages" @@ -26,20 +27,11 @@ func NewGosec(settings *config.GoSecSettings) *goanalysis.Linter { var mu sync.Mutex var resIssues []goanalysis.Issue - conf := gosec.NewConfig() - var filters []rules.RuleFilter + conf := gosec.NewConfig() if settings != nil { filters = gosecRuleFilters(settings.Includes, settings.Excludes) - - for k, v := range settings.Config { - if k != gosec.Globals { - // Uses ToUpper because the parsing of the map's key change the key to lowercase. - // The value is not impacted by that: the case is respected. - k = strings.ToUpper(k) - } - conf.Set(k, v) - } + conf = toGosecConfig(settings) } logger := log.New(io.Discard, "", 0) @@ -58,7 +50,7 @@ func NewGosec(settings *config.GoSecSettings) *goanalysis.Linter { []*analysis.Analyzer{analyzer}, nil, ).WithContextSetter(func(lintCtx *linter.Context) { - analyzer.Run = func(pass *analysis.Pass) (interface{}, error) { + 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()) @@ -84,7 +76,7 @@ func runGoSec(lintCtx *linter.Context, pass *analysis.Pass, settings *config.GoS TypesInfo: pass.TypesInfo, } - analyzer.Check(pkg) + analyzer.CheckRules(pkg) secIssues, _, _ := analyzer.Report() if len(secIssues) == 0 { @@ -140,6 +132,35 @@ func runGoSec(lintCtx *linter.Context, pass *analysis.Pass, settings *config.GoS return issues } +func toGosecConfig(settings *config.GoSecSettings) gosec.Config { + conf := gosec.NewConfig() + + for k, v := range settings.Config { + if k == gosec.Globals { + convertGosecGlobals(v, conf) + continue + } + + // Uses ToUpper because the parsing of the map's key change the key to lowercase. + // The value is not impacted by that: the case is respected. + conf.Set(strings.ToUpper(k), v) + } + + return conf +} + +// based on https://github.com/securego/gosec/blob/47bfd4eb6fc7395940933388550b547538b4c946/config.go#L52-L62 +func convertGosecGlobals(globalOptionFromConfig any, conf gosec.Config) { + globalOptionMap, ok := globalOptionFromConfig.(map[string]any) + if !ok { + return + } + + for k, v := range globalOptionMap { + conf.SetGlobal(gosec.GlobalOption(k), fmt.Sprintf("%v", v)) + } +} + // based on https://github.com/securego/gosec/blob/569328eade2ccbad4ce2d0f21ee158ab5356a5cf/cmd/gosec/main.go#L170-L188 func gosecRuleFilters(includes, excludes []string) []rules.RuleFilter { var filters []rules.RuleFilter @@ -156,27 +177,29 @@ func gosecRuleFilters(includes, excludes []string) []rules.RuleFilter { } // code borrowed from https://github.com/securego/gosec/blob/69213955dacfd560562e780f723486ef1ca6d486/cmd/gosec/main.go#L250-L262 -func convertToScore(str string) (gosec.Score, error) { +func convertToScore(str string) (issue.Score, error) { str = strings.ToLower(str) switch str { case "", "low": - return gosec.Low, nil + return issue.Low, nil case "medium": - return gosec.Medium, nil + return issue.Medium, nil case "high": - return gosec.High, nil + return issue.High, nil default: - return gosec.Low, fmt.Errorf("'%s' is invalid, use low instead. Valid options: low, medium, high", str) + return issue.Low, fmt.Errorf("'%s' is invalid, use low instead. Valid options: low, medium, high", str) } } // code borrowed from https://github.com/securego/gosec/blob/69213955dacfd560562e780f723486ef1ca6d486/cmd/gosec/main.go#L264-L276 -func filterIssues(issues []*gosec.Issue, severity, confidence gosec.Score) []*gosec.Issue { - res := make([]*gosec.Issue, 0) - for _, issue := range issues { - if issue.Severity >= severity && issue.Confidence >= confidence { - res = append(res, issue) +func filterIssues(issues []*issue.Issue, severity, confidence issue.Score) []*issue.Issue { + res := make([]*issue.Issue, 0) + + for _, i := range issues { + if i.Severity >= severity && i.Confidence >= confidence { + res = append(res, i) } } + return res } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/gosmopolitan.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/gosmopolitan.go new file mode 100644 index 00000000..2e01fcc7 --- /dev/null +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/gosmopolitan.go @@ -0,0 +1,32 @@ +package golinters + +import ( + "strings" + + "github.com/xen0n/gosmopolitan" + "golang.org/x/tools/go/analysis" + + "github.com/golangci/golangci-lint/pkg/config" + "github.com/golangci/golangci-lint/pkg/golinters/goanalysis" +) + +func NewGosmopolitan(s *config.GosmopolitanSettings) *goanalysis.Linter { + a := gosmopolitan.NewAnalyzer() + + cfgMap := map[string]map[string]any{} + if s != nil { + cfgMap[a.Name] = map[string]any{ + "allowtimelocal": s.AllowTimeLocal, + "escapehatches": strings.Join(s.EscapeHatches, ","), + "lookattests": !s.IgnoreTests, + "watchforscripts": strings.Join(s.WatchForScripts, ","), + } + } + + return goanalysis.NewLinter( + a.Name, + a.Doc, + []*analysis.Analyzer{a}, + cfgMap, + ).WithLoadMode(goanalysis.LoadModeTypesInfo) +} diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/govet.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/govet.go index afed0821..704dd6c5 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/govet.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/govet.go @@ -123,7 +123,7 @@ var ( ) func NewGovet(settings *config.GovetSettings) *goanalysis.Linter { - var conf map[string]map[string]interface{} + var conf map[string]map[string]any if settings != nil { conf = settings.Settings } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/grouper.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/grouper.go index e8c1340e..9feecf3b 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/grouper.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/grouper.go @@ -9,9 +9,9 @@ import ( ) func NewGrouper(settings *config.GrouperSettings) *goanalysis.Linter { - linterCfg := map[string]map[string]interface{}{} + linterCfg := map[string]map[string]any{} if settings != nil { - linterCfg["grouper"] = map[string]interface{}{ + linterCfg["grouper"] = map[string]any{ "const-require-single-const": settings.ConstRequireSingleConst, "const-require-grouping": settings.ConstRequireGrouping, "import-require-single-import": settings.ImportRequireSingleImport, diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/ifshort.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/ifshort.go index c26f08e4..1574eaf7 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/ifshort.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/ifshort.go @@ -9,9 +9,9 @@ import ( ) func NewIfshort(settings *config.IfshortSettings) *goanalysis.Linter { - var cfg map[string]map[string]interface{} + var cfg map[string]map[string]any if settings != nil { - cfg = map[string]map[string]interface{}{ + cfg = map[string]map[string]any{ analyzer.Analyzer.Name: { "max-decl-lines": settings.MaxDeclLines, "max-decl-chars": settings.MaxDeclChars, diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/importas.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/importas.go index 1917bbb0..c7d8c175 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/importas.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/importas.go @@ -36,12 +36,26 @@ func NewImportAs(settings *config.ImportAsSettings) *goanalysis.Linter { lintCtx.Log.Errorf("failed to parse configuration: %v", err) } + uniqPackages := make(map[string]config.ImportAsAlias) + uniqAliases := make(map[string]config.ImportAsAlias) for _, a := range settings.Alias { if a.Pkg == "" { lintCtx.Log.Errorf("invalid configuration, empty package: pkg=%s alias=%s", a.Pkg, a.Alias) continue } + if v, ok := uniqPackages[a.Pkg]; ok { + lintCtx.Log.Errorf("invalid configuration, multiple aliases for the same package: pkg=%s aliases=[%s,%s]", a.Pkg, a.Alias, v.Alias) + } else { + uniqPackages[a.Pkg] = a + } + + if v, ok := uniqAliases[a.Alias]; ok { + lintCtx.Log.Errorf("invalid configuration, multiple packages with the same alias: alias=%s packages=[%s,%s]", a.Alias, a.Pkg, v.Pkg) + } else { + uniqAliases[a.Alias] = a + } + err := analyzer.Flags.Set("alias", fmt.Sprintf("%s:%s", a.Pkg, a.Alias)) if err != nil { lintCtx.Log.Errorf("failed to parse configuration: %v", err) diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/interfacebloat.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/interfacebloat.go index 044c96f3..a6dbfe17 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/interfacebloat.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/interfacebloat.go @@ -11,9 +11,9 @@ import ( func NewInterfaceBloat(settings *config.InterfaceBloatSettings) *goanalysis.Linter { a := analyzer.New() - var cfg map[string]map[string]interface{} + var cfg map[string]map[string]any if settings != nil { - cfg = map[string]map[string]interface{}{ + cfg = map[string]map[string]any{ a.Name: { analyzer.InterfaceMaxMethodsFlag: settings.Max, }, diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/interfacer.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/interfacer.go index 59125c5c..71bdfddb 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/interfacer.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/interfacer.go @@ -22,7 +22,7 @@ func NewInterfacer() *goanalysis.Linter { Name: interfacerName, Doc: goanalysis.TheOnlyanalyzerDoc, Requires: []*analysis.Analyzer{buildssa.Analyzer}, - Run: func(pass *analysis.Pass) (interface{}, error) { + Run: func(pass *analysis.Pass) (any, error) { issues, err := runInterfacer(pass) if err != nil { return nil, err diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/ireturn.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/ireturn.go index f2d4aec9..34dc09d2 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/ireturn.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/ireturn.go @@ -13,9 +13,9 @@ import ( func NewIreturn(settings *config.IreturnSettings) *goanalysis.Linter { a := analyzer.NewAnalyzer() - cfg := map[string]map[string]interface{}{} + cfg := map[string]map[string]any{} if settings != nil { - cfg[a.Name] = map[string]interface{}{ + cfg[a.Name] = map[string]any{ "allow": strings.Join(settings.Allow, ","), "reject": strings.Join(settings.Reject, ","), } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/lll.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/lll.go index 2e0ebdee..9ed32012 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/lll.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/lll.go @@ -29,7 +29,7 @@ func NewLLL(settings *config.LllSettings) *goanalysis.Linter { analyzer := &analysis.Analyzer{ Name: lllName, Doc: goanalysis.TheOnlyanalyzerDoc, - Run: func(pass *analysis.Pass) (interface{}, error) { + Run: func(pass *analysis.Pass) (any, error) { issues, err := runLll(pass, settings) if err != nil { return nil, err diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/maintidx.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/maintidx.go index 183006b0..55509d97 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/maintidx.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/maintidx.go @@ -11,12 +11,12 @@ import ( func NewMaintIdx(cfg *config.MaintIdxSettings) *goanalysis.Linter { analyzer := maintidx.Analyzer - cfgMap := map[string]map[string]interface{}{ + cfgMap := map[string]map[string]any{ analyzer.Name: {"under": 20}, } if cfg != nil { - cfgMap[analyzer.Name] = map[string]interface{}{ + cfgMap[analyzer.Name] = map[string]any{ "under": cfg.Under, } } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/makezero.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/makezero.go index 2125b4be..a9828629 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/makezero.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/makezero.go @@ -23,7 +23,7 @@ func NewMakezero(settings *config.MakezeroSettings) *goanalysis.Linter { analyzer := &analysis.Analyzer{ Name: makezeroName, Doc: goanalysis.TheOnlyanalyzerDoc, - Run: func(pass *analysis.Pass) (interface{}, error) { + Run: func(pass *analysis.Pass) (any, error) { issues, err := runMakeZero(pass, settings) if err != nil { return nil, err diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/maligned.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/maligned.go index 9c3ca8b5..0455be76 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/maligned.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/maligned.go @@ -23,7 +23,7 @@ func NewMaligned(settings *config.MalignedSettings) *goanalysis.Linter { analyzer := &analysis.Analyzer{ Name: malignedName, Doc: goanalysis.TheOnlyanalyzerDoc, - Run: func(pass *analysis.Pass) (interface{}, error) { + Run: func(pass *analysis.Pass) (any, error) { issues := runMaligned(pass, settings) if len(issues) == 0 { diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/mirror.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/mirror.go new file mode 100644 index 00000000..4adc001a --- /dev/null +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/mirror.go @@ -0,0 +1,70 @@ +package golinters + +import ( + "sync" + + "github.com/butuzov/mirror" + "golang.org/x/tools/go/analysis" + + "github.com/golangci/golangci-lint/pkg/golinters/goanalysis" + "github.com/golangci/golangci-lint/pkg/lint/linter" + "github.com/golangci/golangci-lint/pkg/result" +) + +func NewMirror() *goanalysis.Linter { + var ( + mu sync.Mutex + issues []goanalysis.Issue + ) + + a := mirror.NewAnalyzer() + a.Run = func(pass *analysis.Pass) (any, error) { + // mirror only lints test files if the `--with-tests` flag is passed, + // so we pass the `with-tests` flag as true to the analyzer before running it. + // This can be turned off by using the regular golangci-lint flags such as `--tests` or `--skip-files` + // or can be disabled per linter via exclude rules. + // (see https://github.com/golangci/golangci-lint/issues/2527#issuecomment-1023707262) + violations := mirror.Run(pass, true) + + if len(violations) == 0 { + return nil, nil + } + + for index := range violations { + i := violations[index].Issue(pass.Fset) + + issue := result.Issue{ + FromLinter: a.Name, + Text: i.Message, + Pos: i.Start, + } + + if len(i.InlineFix) > 0 { + issue.Replacement = &result.Replacement{ + Inline: &result.InlineFix{ + StartCol: i.Start.Column - 1, + Length: len(i.Original), + NewString: i.InlineFix, + }, + } + } + + mu.Lock() + issues = append(issues, goanalysis.NewIssue(&issue, pass)) + mu.Unlock() + } + + return nil, nil + } + + analyzer := goanalysis.NewLinter( + a.Name, + a.Doc, + []*analysis.Analyzer{a}, + nil, + ).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue { + return issues + }).WithLoadMode(goanalysis.LoadModeTypesInfo) + + return analyzer +} diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/misspell.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/misspell.go index b5cc5c8a..ce2b79a7 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/misspell.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/misspell.go @@ -35,7 +35,7 @@ func NewMisspell(settings *config.MisspellSettings) *goanalysis.Linter { ).WithContextSetter(func(lintCtx *linter.Context) { replacer, ruleErr := createMisspellReplacer(settings) - analyzer.Run = func(pass *analysis.Pass) (interface{}, error) { + analyzer.Run = func(pass *analysis.Pass) (any, error) { if ruleErr != nil { return nil, ruleErr } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/musttag.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/musttag.go index 75500b5a..d9ea7efc 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/musttag.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/musttag.go @@ -1,7 +1,7 @@ package golinters import ( - "github.com/junk1tm/musttag" + "go.tmz.dev/musttag" "golang.org/x/tools/go/analysis" "github.com/golangci/golangci-lint/pkg/config" diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/nakedret.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/nakedret.go index dc2de034..d276ac6a 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/nakedret.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/nakedret.go @@ -1,134 +1,27 @@ package golinters import ( - "fmt" - "go/ast" - "go/token" - "sync" - + "github.com/alexkohler/nakedret/v2" "golang.org/x/tools/go/analysis" "github.com/golangci/golangci-lint/pkg/config" "github.com/golangci/golangci-lint/pkg/golinters/goanalysis" - "github.com/golangci/golangci-lint/pkg/lint/linter" - "github.com/golangci/golangci-lint/pkg/result" ) const nakedretName = "nakedret" -//nolint:dupl func NewNakedret(settings *config.NakedretSettings) *goanalysis.Linter { - var mu sync.Mutex - var resIssues []goanalysis.Issue - - analyzer := &analysis.Analyzer{ - Name: nakedretName, - Doc: goanalysis.TheOnlyanalyzerDoc, - Run: func(pass *analysis.Pass) (interface{}, error) { - issues := runNakedRet(pass, settings) - - if len(issues) == 0 { - return nil, nil - } - - mu.Lock() - resIssues = append(resIssues, issues...) - mu.Unlock() - - return nil, nil - }, + var maxLines int + if settings != nil { + maxLines = settings.MaxFuncLines } + analyzer := nakedret.NakedReturnAnalyzer(uint(maxLines)) + return goanalysis.NewLinter( nakedretName, "Finds naked returns in functions greater than a specified function length", []*analysis.Analyzer{analyzer}, nil, - ).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue { - return resIssues - }).WithLoadMode(goanalysis.LoadModeSyntax) -} - -func runNakedRet(pass *analysis.Pass, settings *config.NakedretSettings) []goanalysis.Issue { - var issues []goanalysis.Issue - - for _, file := range pass.Files { - v := nakedretVisitor{ - maxLength: settings.MaxFuncLines, - f: pass.Fset, - } - - ast.Walk(&v, file) - - for i := range v.issues { - issues = append(issues, goanalysis.NewIssue(&v.issues[i], pass)) - } - } - - return issues -} - -type nakedretVisitor struct { - maxLength int - f *token.FileSet - issues []result.Issue -} - -func (v *nakedretVisitor) processFuncDecl(funcDecl *ast.FuncDecl) { - file := v.f.File(funcDecl.Pos()) - functionLineLength := file.Position(funcDecl.End()).Line - file.Position(funcDecl.Pos()).Line - - // Scan the body for usage of the named returns - for _, stmt := range funcDecl.Body.List { - s, ok := stmt.(*ast.ReturnStmt) - if !ok { - continue - } - - if len(s.Results) != 0 { - continue - } - - file := v.f.File(s.Pos()) - if file == nil || functionLineLength <= v.maxLength { - continue - } - if funcDecl.Name == nil { - continue - } - - v.issues = append(v.issues, result.Issue{ - FromLinter: nakedretName, - Text: fmt.Sprintf("naked return in func `%s` with %d lines of code", - funcDecl.Name.Name, functionLineLength), - Pos: v.f.Position(s.Pos()), - }) - } -} - -func (v *nakedretVisitor) Visit(node ast.Node) ast.Visitor { - funcDecl, ok := node.(*ast.FuncDecl) - if !ok { - return v - } - - var namedReturns []*ast.Ident - - // We've found a function - if funcDecl.Type != nil && funcDecl.Type.Results != nil { - for _, field := range funcDecl.Type.Results.List { - for _, ident := range field.Names { - if ident != nil { - namedReturns = append(namedReturns, ident) - } - } - } - } - - if len(namedReturns) == 0 || funcDecl.Body == nil { - return v - } - - v.processFuncDecl(funcDecl) - return v + ).WithLoadMode(goanalysis.LoadModeSyntax) } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/nestif.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/nestif.go index 78a516f9..12ad69ec 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/nestif.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/nestif.go @@ -23,7 +23,7 @@ func NewNestif(settings *config.NestifSettings) *goanalysis.Linter { analyzer := &analysis.Analyzer{ Name: goanalysis.TheOnlyAnalyzerName, Doc: goanalysis.TheOnlyanalyzerDoc, - Run: func(pass *analysis.Pass) (interface{}, error) { + Run: func(pass *analysis.Pass) (any, error) { issues := runNestIf(pass, settings) if len(issues) == 0 { diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/nilnil.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/nilnil.go index 739b4d4f..804557b7 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/nilnil.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/nilnil.go @@ -13,9 +13,9 @@ import ( func NewNilNil(cfg *config.NilNilSettings) *goanalysis.Linter { a := analyzer.New() - cfgMap := make(map[string]map[string]interface{}) + cfgMap := make(map[string]map[string]any) if cfg != nil && len(cfg.CheckedTypes) != 0 { - cfgMap[a.Name] = map[string]interface{}{ + cfgMap[a.Name] = map[string]any{ "checked-types": strings.Join(cfg.CheckedTypes, ","), } } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/nlreturn.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/nlreturn.go index fb4919f8..a359548f 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/nlreturn.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/nlreturn.go @@ -11,9 +11,9 @@ import ( func NewNLReturn(settings *config.NlreturnSettings) *goanalysis.Linter { a := nlreturn.NewAnalyzer() - cfg := map[string]map[string]interface{}{} + cfg := map[string]map[string]any{} if settings != nil { - cfg[a.Name] = map[string]interface{}{ + cfg[a.Name] = map[string]any{ "block-size": settings.BlockSize, } } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/nolintlint.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/nolintlint.go index a809f449..00ef1f83 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/nolintlint.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/nolintlint.go @@ -24,7 +24,7 @@ func NewNoLintLint(settings *config.NoLintLintSettings) *goanalysis.Linter { analyzer := &analysis.Analyzer{ Name: NoLintLintName, Doc: goanalysis.TheOnlyanalyzerDoc, - Run: func(pass *analysis.Pass) (interface{}, error) { + Run: func(pass *analysis.Pass) (any, error) { issues, err := runNoLintLint(pass, settings) if err != nil { return nil, err diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/nonamedreturns.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/nonamedreturns.go index 3dff2f75..7856f6d6 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/nonamedreturns.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/nonamedreturns.go @@ -11,9 +11,9 @@ import ( func NewNoNamedReturns(settings *config.NoNamedReturnsSettings) *goanalysis.Linter { a := analyzer.Analyzer - var cfg map[string]map[string]interface{} + var cfg map[string]map[string]any if settings != nil { - cfg = map[string]map[string]interface{}{ + cfg = map[string]map[string]any{ a.Name: { analyzer.FlagReportErrorInDefer: settings.ReportErrorInDefer, }, diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/paralleltest.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/paralleltest.go index 55af7350..92201e4e 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/paralleltest.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/paralleltest.go @@ -11,9 +11,9 @@ import ( func NewParallelTest(settings *config.ParallelTestSettings) *goanalysis.Linter { a := paralleltest.Analyzer - var cfg map[string]map[string]interface{} + var cfg map[string]map[string]any if settings != nil { - cfg = map[string]map[string]interface{}{ + cfg = map[string]map[string]any{ a.Name: { "i": settings.IgnoreMissing, }, diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/prealloc.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/prealloc.go index 75a9b4ec..f48d5756 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/prealloc.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/prealloc.go @@ -23,7 +23,7 @@ func NewPreAlloc(settings *config.PreallocSettings) *goanalysis.Linter { analyzer := &analysis.Analyzer{ Name: preallocName, Doc: goanalysis.TheOnlyanalyzerDoc, - Run: func(pass *analysis.Pass) (interface{}, error) { + Run: func(pass *analysis.Pass) (any, error) { issues := runPreAlloc(pass, settings) if len(issues) == 0 { diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/predeclared.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/predeclared.go index caccd482..d3c25e27 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/predeclared.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/predeclared.go @@ -11,9 +11,9 @@ import ( func NewPredeclared(settings *config.PredeclaredSettings) *goanalysis.Linter { a := predeclared.Analyzer - var cfg map[string]map[string]interface{} + var cfg map[string]map[string]any if settings != nil { - cfg = map[string]map[string]interface{}{ + cfg = map[string]map[string]any{ a.Name: { predeclared.IgnoreFlag: settings.Ignore, predeclared.QualifiedFlag: settings.Qualified, diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/promlinter.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/promlinter.go index f77847a4..381c5748 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/promlinter.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/promlinter.go @@ -30,7 +30,7 @@ func NewPromlinter(settings *config.PromlinterSettings) *goanalysis.Linter { analyzer := &analysis.Analyzer{ Name: promlinterName, Doc: goanalysis.TheOnlyanalyzerDoc, - Run: func(pass *analysis.Pass) (interface{}, error) { + Run: func(pass *analysis.Pass) (any, error) { issues := runPromLinter(pass, promSettings) if len(issues) == 0 { diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/reassign.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/reassign.go index bc1b93a5..a6dd6705 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/reassign.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/reassign.go @@ -14,9 +14,9 @@ import ( func NewReassign(settings *config.ReassignSettings) *goanalysis.Linter { a := reassign.NewAnalyzer() - var cfg map[string]map[string]interface{} + var cfg map[string]map[string]any if settings != nil && len(settings.Patterns) > 0 { - cfg = map[string]map[string]interface{}{ + cfg = map[string]map[string]any{ a.Name: { reassign.FlagPattern: fmt.Sprintf("^(%s)$", strings.Join(settings.Patterns, "|")), }, diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/revive.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/revive.go index 13976547..b57566e7 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/revive.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/revive.go @@ -51,7 +51,7 @@ func NewRevive(settings *config.ReviveSettings) *goanalysis.Linter { []*analysis.Analyzer{analyzer}, nil, ).WithContextSetter(func(lintCtx *linter.Context) { - analyzer.Run = func(pass *analysis.Pass) (interface{}, error) { + analyzer.Run = func(pass *analysis.Pass) (any, error) { issues, err := runRevive(lintCtx, pass, settings) if err != nil { return nil, err @@ -187,8 +187,8 @@ func getReviveConfig(cfg *config.ReviveSettings) (*lint.Config, error) { return conf, nil } -func createConfigMap(cfg *config.ReviveSettings) map[string]interface{} { - rawRoot := map[string]interface{}{ +func createConfigMap(cfg *config.ReviveSettings) map[string]any { + rawRoot := map[string]any{ "ignoreGeneratedHeader": cfg.IgnoreGeneratedHeader, "confidence": cfg.Confidence, "severity": cfg.Severity, @@ -197,9 +197,9 @@ func createConfigMap(cfg *config.ReviveSettings) map[string]interface{} { "enableAllRules": cfg.EnableAllRules, } - rawDirectives := map[string]map[string]interface{}{} + rawDirectives := map[string]map[string]any{} for _, directive := range cfg.Directives { - rawDirectives[directive.Name] = map[string]interface{}{ + rawDirectives[directive.Name] = map[string]any{ "severity": directive.Severity, } } @@ -208,9 +208,9 @@ func createConfigMap(cfg *config.ReviveSettings) map[string]interface{} { rawRoot["directive"] = rawDirectives } - rawRules := map[string]map[string]interface{}{} + rawRules := map[string]map[string]any{} for _, s := range cfg.Rules { - rawRules[s.Name] = map[string]interface{}{ + rawRules[s.Name] = map[string]any{ "severity": s.Severity, "arguments": safeTomlSlice(s.Arguments), "disabled": s.Disabled, @@ -224,19 +224,19 @@ func createConfigMap(cfg *config.ReviveSettings) map[string]interface{} { return rawRoot } -func safeTomlSlice(r []interface{}) []interface{} { +func safeTomlSlice(r []any) []any { if len(r) == 0 { return nil } - if _, ok := r[0].(map[interface{}]interface{}); !ok { + if _, ok := r[0].(map[any]any); !ok { return r } - var typed []interface{} + var typed []any for _, elt := range r { - item := map[string]interface{}{} - for k, v := range elt.(map[interface{}]interface{}) { + item := map[string]any{} + for k, v := range elt.(map[any]any) { item[k.(string)] = v } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/scopelint.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/scopelint.go index 7054ef33..e6ef15ed 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/scopelint.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/scopelint.go @@ -23,7 +23,7 @@ func NewScopelint() *goanalysis.Linter { analyzer := &analysis.Analyzer{ Name: scopelintName, Doc: goanalysis.TheOnlyanalyzerDoc, - Run: func(pass *analysis.Pass) (interface{}, error) { + Run: func(pass *analysis.Pass) (any, error) { issues := runScopeLint(pass) if len(issues) == 0 { @@ -176,12 +176,12 @@ func (f *Node) Visit(node ast.Node) ast.Visitor { // and must end with a format string and any arguments. // //nolint:interfacer -func (f *Node) errorf(n ast.Node, format string, args ...interface{}) { +func (f *Node) errorf(n ast.Node, format string, args ...any) { pos := f.fset.Position(n.Pos()) f.errorAtf(pos, format, args...) } -func (f *Node) errorAtf(pos token.Position, format string, args ...interface{}) { +func (f *Node) errorAtf(pos token.Position, format string, args ...any) { *f.issues = append(*f.issues, result.Issue{ Pos: pos, Text: fmt.Sprintf(format, args...), diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/structcheck.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/structcheck.go index fe49b1be..f3df0c2f 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/structcheck.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/structcheck.go @@ -23,7 +23,7 @@ func NewStructcheck(settings *config.StructCheckSettings) *goanalysis.Linter { analyzer := &analysis.Analyzer{ Name: structcheckName, Doc: goanalysis.TheOnlyanalyzerDoc, - Run: func(pass *analysis.Pass) (interface{}, error) { + Run: func(pass *analysis.Pass) (any, error) { issues := runStructCheck(pass, settings) if len(issues) == 0 { diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/stylecheck.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/stylecheck.go index 899f6ff5..2e1e21c5 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/stylecheck.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/stylecheck.go @@ -15,7 +15,7 @@ func NewStylecheck(settings *config.StaticCheckSettings) *goanalysis.Linter { // `scconfig.Analyzer` is a singleton, then it's not possible to have more than one instance for all staticcheck "sub-linters". // When we will merge the 4 "sub-linters", the problem will disappear: https://github.com/golangci/golangci-lint/issues/357 // Currently only stylecheck analyzer has a configuration in staticcheck. - scconfig.Analyzer.Run = func(pass *analysis.Pass) (interface{}, error) { + scconfig.Analyzer.Run = func(pass *analysis.Pass) (any, error) { return cfg, nil } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/tagalign.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/tagalign.go new file mode 100644 index 00000000..07b75646 --- /dev/null +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/tagalign.go @@ -0,0 +1,70 @@ +package golinters + +import ( + "sync" + + "github.com/4meepo/tagalign" + "golang.org/x/tools/go/analysis" + + "github.com/golangci/golangci-lint/pkg/config" + "github.com/golangci/golangci-lint/pkg/golinters/goanalysis" + "github.com/golangci/golangci-lint/pkg/lint/linter" + "github.com/golangci/golangci-lint/pkg/result" +) + +func NewTagAlign(settings *config.TagAlignSettings) *goanalysis.Linter { + var mu sync.Mutex + var resIssues []goanalysis.Issue + + options := []tagalign.Option{tagalign.WithMode(tagalign.GolangciLintMode)} + + if settings != nil { + options = append(options, tagalign.WithAlign(settings.Align)) + + if settings.Sort || len(settings.Order) > 0 { + options = append(options, tagalign.WithSort(settings.Order...)) + } + } + + analyzer := tagalign.NewAnalyzer(options...) + analyzer.Run = func(pass *analysis.Pass) (any, error) { + taIssues := tagalign.Run(pass, options...) + + issues := make([]goanalysis.Issue, len(taIssues)) + for i, issue := range taIssues { + report := &result.Issue{ + FromLinter: analyzer.Name, + Pos: issue.Pos, + Text: issue.Message, + Replacement: &result.Replacement{ + Inline: &result.InlineFix{ + StartCol: issue.InlineFix.StartCol, + Length: issue.InlineFix.Length, + NewString: issue.InlineFix.NewString, + }, + }, + } + + issues[i] = goanalysis.NewIssue(report, pass) + } + + if len(issues) == 0 { + return nil, nil + } + + mu.Lock() + resIssues = append(resIssues, issues...) + mu.Unlock() + + return nil, nil + } + + return goanalysis.NewLinter( + analyzer.Name, + analyzer.Doc, + []*analysis.Analyzer{analyzer}, + nil, + ).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue { + return resIssues + }).WithLoadMode(goanalysis.LoadModeSyntax) +} diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/tenv.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/tenv.go index 174b0dd6..6c6bd318 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/tenv.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/tenv.go @@ -11,9 +11,9 @@ import ( func NewTenv(settings *config.TenvSettings) *goanalysis.Linter { a := tenv.Analyzer - var cfg map[string]map[string]interface{} + var cfg map[string]map[string]any if settings != nil { - cfg = map[string]map[string]interface{}{ + cfg = map[string]map[string]any{ a.Name: { tenv.A: settings.All, }, diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/testpackage.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/testpackage.go index 2cc62759..db1ead96 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/testpackage.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/testpackage.go @@ -13,9 +13,9 @@ import ( func NewTestpackage(cfg *config.TestpackageSettings) *goanalysis.Linter { var a = testpackage.NewAnalyzer() - var settings map[string]map[string]interface{} + var settings map[string]map[string]any if cfg != nil { - settings = map[string]map[string]interface{}{ + settings = map[string]map[string]any{ a.Name: { testpackage.SkipRegexpFlagName: cfg.SkipRegexp, testpackage.AllowPackagesFlagName: strings.Join(cfg.AllowPackages, ","), diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/thelper.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/thelper.go index 9d6b24bd..84a8e9e8 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/thelper.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/thelper.go @@ -47,7 +47,7 @@ func NewThelper(cfg *config.ThelperSettings) *goanalysis.Linter { args = append(args, k) } - cfgMap := map[string]map[string]interface{}{ + cfgMap := map[string]map[string]any{ a.Name: { "checks": strings.Join(args, ","), }, diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/unconvert.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/unconvert.go index def9f156..aad858df 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/unconvert.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/unconvert.go @@ -21,7 +21,7 @@ func NewUnconvert() *goanalysis.Linter { analyzer := &analysis.Analyzer{ Name: unconvertName, Doc: goanalysis.TheOnlyanalyzerDoc, - Run: func(pass *analysis.Pass) (interface{}, error) { + Run: func(pass *analysis.Pass) (any, error) { issues := runUnconvert(pass) if len(issues) == 0 { diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/unparam.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/unparam.go index 7accf295..4078d949 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/unparam.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/unparam.go @@ -24,7 +24,7 @@ func NewUnparam(settings *config.UnparamSettings) *goanalysis.Linter { Name: unparamName, Doc: goanalysis.TheOnlyanalyzerDoc, Requires: []*analysis.Analyzer{buildssa.Analyzer}, - Run: func(pass *analysis.Pass) (interface{}, error) { + Run: func(pass *analysis.Pass) (any, error) { issues, err := runUnparam(pass, settings) if err != nil { return nil, err diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/unused.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/unused.go index d4646905..aa9374d3 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/unused.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/unused.go @@ -27,7 +27,7 @@ func NewUnused(settings *config.StaticCheckSettings) *goanalysis.Linter { Name: unusedName, Doc: unused.Analyzer.Analyzer.Doc, Requires: unused.Analyzer.Analyzer.Requires, - Run: func(pass *analysis.Pass) (interface{}, error) { + Run: func(pass *analysis.Pass) (any, error) { issues, err := runUnused(pass) if err != nil { return nil, err diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/usestdlibvars.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/usestdlibvars.go index 0ea4b563..663a841a 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/usestdlibvars.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/usestdlibvars.go @@ -11,9 +11,9 @@ import ( func NewUseStdlibVars(cfg *config.UseStdlibVarsSettings) *goanalysis.Linter { a := analyzer.New() - cfgMap := make(map[string]map[string]interface{}) + cfgMap := make(map[string]map[string]any) if cfg != nil { - cfgMap[a.Name] = map[string]interface{}{ + cfgMap[a.Name] = map[string]any{ analyzer.ConstantKindFlag: cfg.ConstantKind, analyzer.CryptoHashFlag: cfg.CryptoHash, analyzer.HTTPMethodFlag: cfg.HTTPMethod, diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/varcheck.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/varcheck.go index c2c5b7aa..495c5b59 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/varcheck.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/varcheck.go @@ -31,7 +31,7 @@ func NewVarcheck(settings *config.VarCheckSettings) *goanalysis.Linter { []*analysis.Analyzer{analyzer}, nil, ).WithContextSetter(func(lintCtx *linter.Context) { - analyzer.Run = func(pass *analysis.Pass) (interface{}, error) { + analyzer.Run = func(pass *analysis.Pass) (any, error) { issues := runVarCheck(pass, settings) if len(issues) == 0 { diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/varnamelen.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/varnamelen.go index d86c04b2..688dfa80 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/varnamelen.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/varnamelen.go @@ -13,10 +13,10 @@ import ( func NewVarnamelen(settings *config.VarnamelenSettings) *goanalysis.Linter { analyzer := varnamelen.NewAnalyzer() - cfg := map[string]map[string]interface{}{} + cfg := map[string]map[string]any{} if settings != nil { - vnlCfg := map[string]interface{}{ + vnlCfg := map[string]any{ "checkReceiver": strconv.FormatBool(settings.CheckReceiver), "checkReturn": strconv.FormatBool(settings.CheckReturn), "checkTypeParam": strconv.FormatBool(settings.CheckTypeParam), diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/whitespace.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/whitespace.go index 1ecbda21..e5941fa5 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/whitespace.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/whitespace.go @@ -41,7 +41,7 @@ func NewWhitespace(settings *config.WhitespaceSettings) *goanalysis.Linter { []*analysis.Analyzer{analyzer}, nil, ).WithContextSetter(func(lintCtx *linter.Context) { - analyzer.Run = func(pass *analysis.Pass) (interface{}, error) { + analyzer.Run = func(pass *analysis.Pass) (any, error) { issues, err := runWhitespace(lintCtx, pass, wsSettings) if err != nil { return nil, err diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/wsl.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/wsl.go index 4bc70244..05697a62 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/golinters/wsl.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/wsl.go @@ -40,7 +40,7 @@ func NewWSL(settings *config.WSLSettings) *goanalysis.Linter { analyzer := &analysis.Analyzer{ Name: goanalysis.TheOnlyAnalyzerName, Doc: goanalysis.TheOnlyanalyzerDoc, - Run: func(pass *analysis.Pass) (interface{}, error) { + Run: func(pass *analysis.Pass) (any, error) { issues := runWSL(pass, &conf) if len(issues) == 0 { diff --git a/vendor/github.com/golangci/golangci-lint/pkg/golinters/zerologlint.go b/vendor/github.com/golangci/golangci-lint/pkg/golinters/zerologlint.go new file mode 100644 index 00000000..a37bca12 --- /dev/null +++ b/vendor/github.com/golangci/golangci-lint/pkg/golinters/zerologlint.go @@ -0,0 +1,17 @@ +package golinters + +import ( + "github.com/ykadowak/zerologlint" + "golang.org/x/tools/go/analysis" + + "github.com/golangci/golangci-lint/pkg/golinters/goanalysis" +) + +func NewZerologLint() *goanalysis.Linter { + return goanalysis.NewLinter( + "zerologlint", + "Detects the wrong usage of `zerolog` that a user forgets to dispatch with `Send` or `Msg`.", + []*analysis.Analyzer{zerologlint.Analyzer}, + nil, + ).WithLoadMode(goanalysis.LoadModeTypesInfo) +} diff --git a/vendor/github.com/golangci/golangci-lint/pkg/lint/linter/config.go b/vendor/github.com/golangci/golangci-lint/pkg/lint/linter/config.go index 167ac462..0f546312 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/lint/linter/config.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/lint/linter/config.go @@ -127,7 +127,7 @@ func (lc *Config) WithNoopFallback(cfg *config.Config) *Config { lc.Linter = &Noop{ name: lc.Linter.Name(), desc: lc.Linter.Desc(), - run: func(pass *analysis.Pass) (interface{}, error) { + run: func(pass *analysis.Pass) (any, error) { return nil, nil }, } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/lint/linter/linter.go b/vendor/github.com/golangci/golangci-lint/pkg/lint/linter/linter.go index 7d3b2260..a65d6b92 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/lint/linter/linter.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/lint/linter/linter.go @@ -17,7 +17,7 @@ type Linter interface { type Noop struct { name string desc string - run func(pass *analysis.Pass) (interface{}, error) + run func(pass *analysis.Pass) (any, error) } func (n Noop) Run(_ context.Context, lintCtx *Context) ([]result.Issue, error) { diff --git a/vendor/github.com/golangci/golangci-lint/pkg/lint/lintersdb/manager.go b/vendor/github.com/golangci/golangci-lint/pkg/lint/lintersdb/manager.go index 61a79f6f..17ce2d9c 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/lint/lintersdb/manager.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/lint/lintersdb/manager.go @@ -135,6 +135,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { gomodguardCfg *config.GoModGuardSettings gosecCfg *config.GoSecSettings gosimpleCfg *config.StaticCheckSettings + gosmopolitanCfg *config.GosmopolitanSettings govetCfg *config.GovetSettings grouperCfg *config.GrouperSettings ifshortCfg *config.IfshortSettings @@ -164,6 +165,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { staticcheckCfg *config.StaticCheckSettings structcheckCfg *config.StructCheckSettings stylecheckCfg *config.StaticCheckSettings + tagalignCfg *config.TagAlignSettings tagliatelleCfg *config.TagliatelleSettings tenvCfg *config.TenvSettings testpackageCfg *config.TestpackageSettings @@ -213,6 +215,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { gomodguardCfg = &m.cfg.LintersSettings.Gomodguard gosecCfg = &m.cfg.LintersSettings.Gosec gosimpleCfg = &m.cfg.LintersSettings.Gosimple + gosmopolitanCfg = &m.cfg.LintersSettings.Gosmopolitan govetCfg = &m.cfg.LintersSettings.Govet grouperCfg = &m.cfg.LintersSettings.Grouper ifshortCfg = &m.cfg.LintersSettings.Ifshort @@ -242,12 +245,14 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { staticcheckCfg = &m.cfg.LintersSettings.Staticcheck structcheckCfg = &m.cfg.LintersSettings.Structcheck stylecheckCfg = &m.cfg.LintersSettings.Stylecheck + tagalignCfg = &m.cfg.LintersSettings.TagAlign tagliatelleCfg = &m.cfg.LintersSettings.Tagliatelle tenvCfg = &m.cfg.LintersSettings.Tenv testpackageCfg = &m.cfg.LintersSettings.Testpackage thelperCfg = &m.cfg.LintersSettings.Thelper unparamCfg = &m.cfg.LintersSettings.Unparam unusedCfg = new(config.StaticCheckSettings) + usestdlibvars = &m.cfg.LintersSettings.UseStdlibVars varcheckCfg = &m.cfg.LintersSettings.Varcheck varnamelenCfg = &m.cfg.LintersSettings.Varnamelen whitespaceCfg = &m.cfg.LintersSettings.Whitespace @@ -258,6 +263,10 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { govetCfg.Go = m.cfg.Run.Go } + if gocriticCfg != nil { + gocriticCfg.Go = m.cfg.Run.Go + } + if gofumptCfg != nil && gofumptCfg.LangVersion == "" { gofumptCfg.LangVersion = m.cfg.Run.Go } @@ -305,6 +314,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { linter.NewConfig(golinters.NewContainedCtx()). WithSince("1.44.0"). + WithLoadForGoAnalysis(). WithPresets(linter.PresetStyle). WithURL("https://github.com/sivchari/containedctx"), @@ -417,6 +427,10 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { linter.NewConfig(golinters.NewForbidigo(forbidigoCfg)). WithSince("v1.34.0"). WithPresets(linter.PresetStyle). + // Strictly speaking, + // the additional information is only needed when forbidigoCfg.AnalyzeTypes is chosen by the user. + // But we don't know that here in all cases (sometimes config is not loaded), + // so we have to assume that it is needed to be on the safe side. WithLoadForGoAnalysis(). WithURL("https://github.com/ashanbrown/forbidigo"), @@ -558,6 +572,12 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { WithAlternativeNames(megacheckName). WithURL("https://github.com/dominikh/go-tools/tree/master/simple"), + linter.NewConfig(golinters.NewGosmopolitan(gosmopolitanCfg)). + WithSince("v1.53.0"). + WithLoadForGoAnalysis(). + WithPresets(linter.PresetBugs). + WithURL("https://github.com/xen0n/gosmopolitan"), + linter.NewConfig(golinters.NewGovet(govetCfg)). WithSince("v1.0.0"). WithLoadForGoAnalysis(). @@ -634,6 +654,12 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { WithURL("https://github.com/mdempsky/maligned"). Deprecated("The repository of the linter has been archived by the owner.", "v1.38.0", "govet 'fieldalignment'"), + linter.NewConfig(golinters.NewMirror()). + WithSince("v1.53.0"). + WithPresets(linter.PresetStyle). + WithLoadForGoAnalysis(). + WithURL("https://github.com/butuzov/mirror"), + linter.NewConfig(golinters.NewMisspell(misspellCfg)). WithSince("v1.8.0"). WithPresets(linter.PresetStyle, linter.PresetComment). @@ -644,7 +670,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { WithSince("v1.51.0"). WithLoadForGoAnalysis(). WithPresets(linter.PresetStyle, linter.PresetBugs). - WithURL("https://github.com/junk1tm/musttag"), + WithURL("https://github.com/tmzane/musttag"), linter.NewConfig(golinters.NewNakedret(nakedretCfg)). WithSince("v1.19.0"). @@ -759,8 +785,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { WithLoadForGoAnalysis(). WithPresets(linter.PresetUnused). WithURL("https://github.com/opennota/check"). - Deprecated("The owner seems to have abandoned the linter.", "v1.49.0", "unused"). - WithNoopFallback(m.cfg), + Deprecated("The owner seems to have abandoned the linter.", "v1.49.0", "unused"), linter.NewConfig(golinters.NewStylecheck(stylecheckCfg)). WithSince("v1.20.0"). @@ -768,6 +793,12 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { WithPresets(linter.PresetStyle). WithURL("https://github.com/dominikh/go-tools/tree/master/stylecheck"), + linter.NewConfig(golinters.NewTagAlign(tagalignCfg)). + WithSince("v1.53.0"). + WithPresets(linter.PresetStyle, linter.PresetFormatting). + WithAutoFix(). + WithURL("https://github.com/4meepo/tagalign"), + linter.NewConfig(golinters.NewTagliatelle(tagliatelleCfg)). WithSince("v1.40.0"). WithPresets(linter.PresetStyle). @@ -874,6 +905,12 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { WithSince("v1.26.0"). WithPresets(linter.PresetStyle). WithURL("https://github.com/golangci/golangci-lint/blob/master/pkg/golinters/nolintlint/README.md"), + + linter.NewConfig(golinters.NewZerologLint()). + WithSince("v1.53.0"). + WithPresets(linter.PresetBugs). + WithLoadForGoAnalysis(). + WithURL("https://github.com/ykadowak/zerologlint"), } enabledByDefault := map[string]bool{ diff --git a/vendor/github.com/golangci/golangci-lint/pkg/lint/runner.go b/vendor/github.com/golangci/golangci-lint/pkg/lint/runner.go index b1180438..d270892d 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/lint/runner.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/lint/runner.go @@ -276,10 +276,11 @@ func getExcludeRulesProcessor(cfg *config.Issues, log logutils.Log, files *fsuti for _, r := range cfg.ExcludeRules { excludeRules = append(excludeRules, processors.ExcludeRule{ BaseRule: processors.BaseRule{ - Text: r.Text, - Source: r.Source, - Path: r.Path, - Linters: r.Linters, + Text: r.Text, + Source: r.Source, + Path: r.Path, + PathExcept: r.PathExcept, + Linters: r.Linters, }, }) } @@ -319,10 +320,11 @@ func getSeverityRulesProcessor(cfg *config.Severity, log logutils.Log, files *fs severityRules = append(severityRules, processors.SeverityRule{ Severity: r.Severity, BaseRule: processors.BaseRule{ - Text: r.Text, - Source: r.Source, - Path: r.Path, - Linters: r.Linters, + Text: r.Text, + Source: r.Source, + Path: r.Path, + PathExcept: r.PathExcept, + Linters: r.Linters, }, }) } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/logutils/log.go b/vendor/github.com/golangci/golangci-lint/pkg/logutils/log.go index 2c9609d8..16067e49 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/logutils/log.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/logutils/log.go @@ -1,11 +1,11 @@ package logutils type Log interface { - Fatalf(format string, args ...interface{}) - Panicf(format string, args ...interface{}) - Errorf(format string, args ...interface{}) - Warnf(format string, args ...interface{}) - Infof(format string, args ...interface{}) + Fatalf(format string, args ...any) + Panicf(format string, args ...any) + Errorf(format string, args ...any) + Warnf(format string, args ...any) + Infof(format string, args ...any) Child(name string) Log SetLevel(level LogLevel) diff --git a/vendor/github.com/golangci/golangci-lint/pkg/logutils/logutils.go b/vendor/github.com/golangci/golangci-lint/pkg/logutils/logutils.go index 62c521ea..80c9fed7 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/logutils/logutils.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/logutils/logutils.go @@ -22,6 +22,7 @@ const ( DebugKeyExcludeRules = "exclude_rules" DebugKeyExec = "exec" DebugKeyFilenameUnadjuster = "filename_unadjuster" + DebugKeyForbidigo = "forbidigo" DebugKeyGoEnv = "goenv" DebugKeyLinter = "linter" DebugKeyLintersContext = "linters_context" @@ -77,9 +78,9 @@ func getEnabledDebugs() map[string]bool { var enabledDebugs = getEnabledDebugs() -type DebugFunc func(format string, args ...interface{}) +type DebugFunc func(format string, args ...any) -func nopDebugf(format string, args ...interface{}) {} +func nopDebugf(_ string, _ ...any) {} func Debug(tag string) DebugFunc { if !enabledDebugs[tag] { @@ -89,7 +90,7 @@ func Debug(tag string) DebugFunc { logger := NewStderrLog(tag) logger.SetLevel(LogLevelDebug) - return func(format string, args ...interface{}) { + return func(format string, args ...any) { logger.Debugf(format, args...) } } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/logutils/mock.go b/vendor/github.com/golangci/golangci-lint/pkg/logutils/mock.go index e897ce1e..efda8cc2 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/logutils/mock.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/logutils/mock.go @@ -12,28 +12,28 @@ func NewMockLog() *MockLog { return &MockLog{} } -func (m *MockLog) Fatalf(format string, args ...interface{}) { - mArgs := []interface{}{format} +func (m *MockLog) Fatalf(format string, args ...any) { + mArgs := []any{format} m.Called(append(mArgs, args...)...) } -func (m *MockLog) Panicf(format string, args ...interface{}) { - mArgs := []interface{}{format} +func (m *MockLog) Panicf(format string, args ...any) { + mArgs := []any{format} m.Called(append(mArgs, args...)...) } -func (m *MockLog) Errorf(format string, args ...interface{}) { - mArgs := []interface{}{format} +func (m *MockLog) Errorf(format string, args ...any) { + mArgs := []any{format} m.Called(append(mArgs, args...)...) } -func (m *MockLog) Warnf(format string, args ...interface{}) { - mArgs := []interface{}{format} +func (m *MockLog) Warnf(format string, args ...any) { + mArgs := []any{format} m.Called(append(mArgs, args...)...) } -func (m *MockLog) Infof(format string, args ...interface{}) { - mArgs := []interface{}{format} +func (m *MockLog) Infof(format string, args ...any) { + mArgs := []any{format} m.Called(append(mArgs, args...)...) } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/logutils/stderr_log.go b/vendor/github.com/golangci/golangci-lint/pkg/logutils/stderr_log.go index a68215e7..367c94f3 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/logutils/stderr_log.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/logutils/stderr_log.go @@ -67,17 +67,17 @@ func (sl StderrLog) prefix() string { return prefix } -func (sl StderrLog) Fatalf(format string, args ...interface{}) { +func (sl StderrLog) Fatalf(format string, args ...any) { sl.logger.Errorf("%s%s", sl.prefix(), fmt.Sprintf(format, args...)) os.Exit(exitcodes.Failure) } -func (sl StderrLog) Panicf(format string, args ...interface{}) { +func (sl StderrLog) Panicf(format string, args ...any) { v := fmt.Sprintf("%s%s", sl.prefix(), fmt.Sprintf(format, args...)) panic(v) } -func (sl StderrLog) Errorf(format string, args ...interface{}) { +func (sl StderrLog) Errorf(format string, args ...any) { if sl.level > LogLevelError { return } @@ -88,7 +88,7 @@ func (sl StderrLog) Errorf(format string, args ...interface{}) { // called on hidden errors, see log levels comments. } -func (sl StderrLog) Warnf(format string, args ...interface{}) { +func (sl StderrLog) Warnf(format string, args ...any) { if sl.level > LogLevelWarn { return } @@ -96,7 +96,7 @@ func (sl StderrLog) Warnf(format string, args ...interface{}) { sl.logger.Warnf("%s%s", sl.prefix(), fmt.Sprintf(format, args...)) } -func (sl StderrLog) Infof(format string, args ...interface{}) { +func (sl StderrLog) Infof(format string, args ...any) { if sl.level > LogLevelInfo { return } @@ -104,7 +104,7 @@ func (sl StderrLog) Infof(format string, args ...interface{}) { sl.logger.Infof("%s%s", sl.prefix(), fmt.Sprintf(format, args...)) } -func (sl StderrLog) Debugf(format string, args ...interface{}) { +func (sl StderrLog) Debugf(format string, args ...any) { if sl.level > LogLevelDebug { return } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/printers/checkstyle.go b/vendor/github.com/golangci/golangci-lint/pkg/printers/checkstyle.go index 307a8e7a..3762ca05 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/printers/checkstyle.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/printers/checkstyle.go @@ -1,7 +1,6 @@ package printers import ( - "context" "encoding/xml" "fmt" "io" @@ -41,7 +40,7 @@ func NewCheckstyle(w io.Writer) *Checkstyle { return &Checkstyle{w: w} } -func (p Checkstyle) Print(ctx context.Context, issues []result.Issue) error { +func (p Checkstyle) Print(issues []result.Issue) error { out := checkstyleOutput{ Version: "5.0", } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/printers/codeclimate.go b/vendor/github.com/golangci/golangci-lint/pkg/printers/codeclimate.go index 8a90f145..55b1c694 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/printers/codeclimate.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/printers/codeclimate.go @@ -1,7 +1,6 @@ package printers import ( - "context" "encoding/json" "fmt" "io" @@ -35,7 +34,7 @@ func NewCodeClimate(w io.Writer) *CodeClimate { return &CodeClimate{w: w} } -func (p CodeClimate) Print(ctx context.Context, issues []result.Issue) error { +func (p CodeClimate) Print(issues []result.Issue) error { codeClimateIssues := make([]CodeClimateIssue, 0, len(issues)) for i := range issues { issue := &issues[i] diff --git a/vendor/github.com/golangci/golangci-lint/pkg/printers/github.go b/vendor/github.com/golangci/golangci-lint/pkg/printers/github.go index 6a4d05d4..7f148097 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/printers/github.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/printers/github.go @@ -1,7 +1,6 @@ package printers import ( - "context" "fmt" "io" @@ -36,7 +35,7 @@ func formatIssueAsGithub(issue *result.Issue) string { return ret } -func (p *github) Print(_ context.Context, issues []result.Issue) error { +func (p *github) Print(issues []result.Issue) error { for ind := range issues { _, err := fmt.Fprintln(p.w, formatIssueAsGithub(&issues[ind])) if err != nil { diff --git a/vendor/github.com/golangci/golangci-lint/pkg/printers/html.go b/vendor/github.com/golangci/golangci-lint/pkg/printers/html.go index 3d82d7d8..7dd1e5c6 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/printers/html.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/printers/html.go @@ -1,7 +1,6 @@ package printers import ( - "context" "fmt" "html/template" "io" @@ -131,7 +130,7 @@ func NewHTML(w io.Writer) *HTML { return &HTML{w: w} } -func (p HTML) Print(_ context.Context, issues []result.Issue) error { +func (p HTML) Print(issues []result.Issue) error { var htmlIssues []htmlIssue for i := range issues { diff --git a/vendor/github.com/golangci/golangci-lint/pkg/printers/json.go b/vendor/github.com/golangci/golangci-lint/pkg/printers/json.go index cfef51f5..4bae526b 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/printers/json.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/printers/json.go @@ -1,7 +1,6 @@ package printers import ( - "context" "encoding/json" "io" @@ -26,7 +25,7 @@ type JSONResult struct { Report *report.Data } -func (p JSON) Print(ctx context.Context, issues []result.Issue) error { +func (p JSON) Print(issues []result.Issue) error { res := JSONResult{ Issues: issues, Report: p.rd, diff --git a/vendor/github.com/golangci/golangci-lint/pkg/printers/junitxml.go b/vendor/github.com/golangci/golangci-lint/pkg/printers/junitxml.go index 0424f78b..86a3811e 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/printers/junitxml.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/printers/junitxml.go @@ -1,7 +1,6 @@ package printers import ( - "context" "encoding/xml" "fmt" "io" @@ -45,7 +44,7 @@ func NewJunitXML(w io.Writer) *JunitXML { return &JunitXML{w: w} } -func (p JunitXML) Print(ctx context.Context, issues []result.Issue) error { +func (p JunitXML) Print(issues []result.Issue) error { suites := make(map[string]testSuiteXML) // use a map to group by file for ind := range issues { diff --git a/vendor/github.com/golangci/golangci-lint/pkg/printers/printer.go b/vendor/github.com/golangci/golangci-lint/pkg/printers/printer.go index bfafb88e..ce3116fa 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/printers/printer.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/printers/printer.go @@ -1,11 +1,9 @@ package printers import ( - "context" - "github.com/golangci/golangci-lint/pkg/result" ) type Printer interface { - Print(ctx context.Context, issues []result.Issue) error + Print(issues []result.Issue) error } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/printers/tab.go b/vendor/github.com/golangci/golangci-lint/pkg/printers/tab.go index ffef4910..8ede8974 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/printers/tab.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/printers/tab.go @@ -1,7 +1,6 @@ package printers import ( - "context" "fmt" "io" "text/tabwriter" @@ -14,24 +13,32 @@ import ( type Tab struct { printLinterName bool - log logutils.Log - w io.Writer + useColors bool + + log logutils.Log + w io.Writer } -func NewTab(printLinterName bool, log logutils.Log, w io.Writer) *Tab { +func NewTab(printLinterName, useColors bool, log logutils.Log, w io.Writer) *Tab { return &Tab{ printLinterName: printLinterName, + useColors: useColors, log: log, w: w, } } -func (p *Tab) SprintfColored(ca color.Attribute, format string, args ...interface{}) string { +func (p *Tab) SprintfColored(ca color.Attribute, format string, args ...any) string { c := color.New(ca) + + if !p.useColors { + c.DisableColor() + } + return c.Sprintf(format, args...) } -func (p *Tab) Print(ctx context.Context, issues []result.Issue) error { +func (p *Tab) Print(issues []result.Issue) error { w := tabwriter.NewWriter(p.w, 0, 0, 2, ' ', 0) for i := range issues { diff --git a/vendor/github.com/golangci/golangci-lint/pkg/printers/teamcity.go b/vendor/github.com/golangci/golangci-lint/pkg/printers/teamcity.go index 790f30a2..d3693e99 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/printers/teamcity.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/printers/teamcity.go @@ -1,7 +1,6 @@ package printers import ( - "context" "fmt" "io" "strings" @@ -38,7 +37,7 @@ func NewTeamCity(w io.Writer) *TeamCity { } } -func (p *TeamCity) Print(_ context.Context, issues []result.Issue) error { +func (p *TeamCity) Print(issues []result.Issue) error { uniqLinters := map[string]struct{}{} for i := range issues { diff --git a/vendor/github.com/golangci/golangci-lint/pkg/printers/text.go b/vendor/github.com/golangci/golangci-lint/pkg/printers/text.go index d59391b2..6e29c4b5 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/printers/text.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/printers/text.go @@ -1,7 +1,6 @@ package printers import ( - "context" "fmt" "io" "strings" @@ -14,8 +13,8 @@ import ( type Text struct { printIssuedLine bool - useColors bool printLinterName bool + useColors bool log logutils.Log w io.Writer @@ -24,23 +23,24 @@ type Text struct { func NewText(printIssuedLine, useColors, printLinterName bool, log logutils.Log, w io.Writer) *Text { return &Text{ printIssuedLine: printIssuedLine, - useColors: useColors, printLinterName: printLinterName, + useColors: useColors, log: log, w: w, } } -func (p *Text) SprintfColored(ca color.Attribute, format string, args ...interface{}) string { +func (p *Text) SprintfColored(ca color.Attribute, format string, args ...any) string { + c := color.New(ca) + if !p.useColors { - return fmt.Sprintf(format, args...) + c.DisableColor() } - c := color.New(ca) return c.Sprintf(format, args...) } -func (p *Text) Print(ctx context.Context, issues []result.Issue) error { +func (p *Text) Print(issues []result.Issue) error { for i := range issues { p.printIssue(&issues[i]) @@ -73,7 +73,7 @@ func (p *Text) printSourceCode(i *result.Issue) { } } -func (p Text) printUnderLinePointer(i *result.Issue) { +func (p *Text) printUnderLinePointer(i *result.Issue) { // if column == 0 it means column is unknown (e.g. for gosec) if len(i.SourceLines) != 1 || i.Pos.Column == 0 { return diff --git a/vendor/github.com/golangci/golangci-lint/pkg/report/log.go b/vendor/github.com/golangci/golangci-lint/pkg/report/log.go index 45ab6cae..61665f28 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/report/log.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/report/log.go @@ -20,20 +20,20 @@ func NewLogWrapper(log logutils.Log, reportData *Data) *LogWrapper { } } -func (lw LogWrapper) Fatalf(format string, args ...interface{}) { +func (lw LogWrapper) Fatalf(format string, args ...any) { lw.origLog.Fatalf(format, args...) } -func (lw LogWrapper) Panicf(format string, args ...interface{}) { +func (lw LogWrapper) Panicf(format string, args ...any) { lw.origLog.Panicf(format, args...) } -func (lw LogWrapper) Errorf(format string, args ...interface{}) { +func (lw LogWrapper) Errorf(format string, args ...any) { lw.origLog.Errorf(format, args...) lw.rd.Error = fmt.Sprintf(format, args...) } -func (lw LogWrapper) Warnf(format string, args ...interface{}) { +func (lw LogWrapper) Warnf(format string, args ...any) { lw.origLog.Warnf(format, args...) w := Warning{ Tag: strings.Join(lw.tags, "/"), @@ -43,7 +43,7 @@ func (lw LogWrapper) Warnf(format string, args ...interface{}) { lw.rd.Warnings = append(lw.rd.Warnings, w) } -func (lw LogWrapper) Infof(format string, args ...interface{}) { +func (lw LogWrapper) Infof(format string, args ...any) { lw.origLog.Infof(format, args...) } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/result/processors/base_rule.go b/vendor/github.com/golangci/golangci-lint/pkg/result/processors/base_rule.go index eaed829a..b5e13880 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/result/processors/base_rule.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/result/processors/base_rule.go @@ -9,21 +9,23 @@ import ( ) type BaseRule struct { - Text string - Source string - Path string - Linters []string + Text string + Source string + Path string + PathExcept string + Linters []string } type baseRule struct { - text *regexp.Regexp - source *regexp.Regexp - path *regexp.Regexp - linters []string + text *regexp.Regexp + source *regexp.Regexp + path *regexp.Regexp + pathExcept *regexp.Regexp + linters []string } func (r *baseRule) isEmpty() bool { - return r.text == nil && r.source == nil && r.path == nil && len(r.linters) == 0 + return r.text == nil && r.source == nil && r.path == nil && r.pathExcept == nil && len(r.linters) == 0 } func (r *baseRule) match(issue *result.Issue, files *fsutils.Files, log logutils.Log) bool { @@ -36,6 +38,9 @@ func (r *baseRule) match(issue *result.Issue, files *fsutils.Files, log logutils if r.path != nil && !r.path.MatchString(files.WithPathPrefix(issue.FilePath())) { return false } + if r.pathExcept != nil && r.pathExcept.MatchString(issue.FilePath()) { + return false + } if len(r.linters) != 0 && !r.matchLinter(issue) { return false } diff --git a/vendor/github.com/golangci/golangci-lint/pkg/result/processors/exclude_rules.go b/vendor/github.com/golangci/golangci-lint/pkg/result/processors/exclude_rules.go index 42a24f64..2f7e30b4 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/result/processors/exclude_rules.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/result/processors/exclude_rules.go @@ -47,6 +47,10 @@ func createRules(rules []ExcludeRule, prefix string) []excludeRule { path := fsutils.NormalizePathInRegex(rule.Path) parsedRule.path = regexp.MustCompile(path) } + if rule.PathExcept != "" { + pathExcept := fsutils.NormalizePathInRegex(rule.PathExcept) + parsedRule.pathExcept = regexp.MustCompile(pathExcept) + } parsedRules = append(parsedRules, parsedRule) } return parsedRules diff --git a/vendor/github.com/golangci/golangci-lint/pkg/result/processors/severity_rules.go b/vendor/github.com/golangci/golangci-lint/pkg/result/processors/severity_rules.go index 8174ae8c..0a4a643b 100644 --- a/vendor/github.com/golangci/golangci-lint/pkg/result/processors/severity_rules.go +++ b/vendor/github.com/golangci/golangci-lint/pkg/result/processors/severity_rules.go @@ -52,6 +52,10 @@ func createSeverityRules(rules []SeverityRule, prefix string) []severityRule { path := fsutils.NormalizePathInRegex(rule.Path) parsedRule.path = regexp.MustCompile(path) } + if rule.PathExcept != "" { + pathExcept := fsutils.NormalizePathInRegex(rule.PathExcept) + parsedRule.pathExcept = regexp.MustCompile(pathExcept) + } parsedRules = append(parsedRules, parsedRule) } return parsedRules diff --git a/vendor/github.com/golangci/golangci-lint/pkg/sliceutil/sliceutil.go b/vendor/github.com/golangci/golangci-lint/pkg/sliceutil/sliceutil.go deleted file mode 100644 index cb89e34e..00000000 --- a/vendor/github.com/golangci/golangci-lint/pkg/sliceutil/sliceutil.go +++ /dev/null @@ -1,17 +0,0 @@ -package sliceutil - -// IndexOf get the index of the given value in the given string slice, -// or -1 if not found. -func IndexOf(slice []string, value string) int { - for i, v := range slice { - if v == value { - return i - } - } - return -1 -} - -// Contains check if a string slice contains a value. -func Contains(slice []string, value string) bool { - return IndexOf(slice, value) != -1 -} diff --git a/vendor/github.com/junk1tm/musttag/.goreleaser.yml b/vendor/github.com/junk1tm/musttag/.goreleaser.yml deleted file mode 100644 index 6f85d818..00000000 --- a/vendor/github.com/junk1tm/musttag/.goreleaser.yml +++ /dev/null @@ -1,30 +0,0 @@ -builds: - - main: ./cmd/musttag - env: - - CGO_ENABLED=0 - flags: - - -trimpath - ldflags: - - -s -w -X main.version={{.Version}} - targets: - - darwin_amd64 - - darwin_arm64 - - linux_amd64 - - windows_amd64 - -archives: - - replacements: - darwin: macOS - format_overrides: - - goos: windows - format: zip - -brews: - - tap: - owner: junk1tm - name: homebrew-tap - branch: main - token: "{{ .Env.HOMEBREW_TAP_GITHUB_TOKEN }}" - homepage: "https://github.com/junk1tm/musttag" - description: "A Go linter that enforces field tags in (un)marshaled structs" - license: "MIT" diff --git a/vendor/github.com/junk1tm/musttag/README.md b/vendor/github.com/junk1tm/musttag/README.md deleted file mode 100644 index c04eae7c..00000000 --- a/vendor/github.com/junk1tm/musttag/README.md +++ /dev/null @@ -1,93 +0,0 @@ -# musttag - -[![ci](https://github.com/junk1tm/musttag/actions/workflows/go.yml/badge.svg)](https://github.com/junk1tm/musttag/actions/workflows/go.yml) -[![docs](https://pkg.go.dev/badge/github.com/junk1tm/musttag.svg)](https://pkg.go.dev/github.com/junk1tm/musttag) -[![report](https://goreportcard.com/badge/github.com/junk1tm/musttag)](https://goreportcard.com/report/github.com/junk1tm/musttag) -[![codecov](https://codecov.io/gh/junk1tm/musttag/branch/main/graph/badge.svg)](https://codecov.io/gh/junk1tm/musttag) - -A Go linter that enforces field tags in (un)marshaled structs - -## 📌 About - -`musttag` checks that exported fields of a struct passed to a `Marshal`-like function are annotated with the relevant tag: - -```go -// BAD: -var user struct { - Name string -} -data, err := json.Marshal(user) - -// GOOD: -var user struct { - Name string `json:"name"` -} -data, err := json.Marshal(user) -``` - -The rational from [Uber Style Guide][1]: - -> The serialized form of the structure is a contract between different systems. -> Changes to the structure of the serialized form, including field names, break this contract. -> Specifying field names inside tags makes the contract explicit, -> and it guards against accidentally breaking the contract by refactoring or renaming fields. - -## 🚀 Features - -`musttag` supports these packages out of the box: - -* `encoding/json` -* `encoding/xml` -* `gopkg.in/yaml.v3` -* `github.com/BurntSushi/toml` -* `github.com/mitchellh/mapstructure` -* ...and any [custom one](#custom-packages) - -## 📦 Install - -### Go - -```shell -go install github.com/junk1tm/musttag/cmd/musttag@latest -``` - -### Brew - -```shell -brew install junk1tm/tap/musttag -``` - -### Manual - -Download a prebuilt binary from the [Releases][2] page. - -## 📋 Usage - -As a standalone binary: - -```shell -musttag ./... -``` - -Via `go vet`: - -```shell -go vet -vettool=$(which musttag) ./... -``` - -### Custom packages - -The `-fn=name:tag:argpos` flag can be used to report functions from custom packages, where - -* `name` is the full name of the function, including the package -* `tag` is the struct tag whose presence should be ensured -* `argpos` is the position of the argument to check - -For example, to support the `sqlx.Get` function: - -```shell -musttag -fn="github.com/jmoiron/sqlx.Get:db:1" ./... -``` - -[1]: https://github.com/uber-go/guide/blob/master/style.md#use-field-tags-in-marshaled-structs -[2]: https://github.com/junk1tm/musttag/releases diff --git a/vendor/github.com/kunwardeep/paralleltest/pkg/paralleltest/paralleltest.go b/vendor/github.com/kunwardeep/paralleltest/pkg/paralleltest/paralleltest.go index c7da52a2..9c2fbb98 100644 --- a/vendor/github.com/kunwardeep/paralleltest/pkg/paralleltest/paralleltest.go +++ b/vendor/github.com/kunwardeep/paralleltest/pkg/paralleltest/paralleltest.go @@ -249,7 +249,9 @@ func isTestFunction(funcDecl *ast.FuncDecl) (bool, string) { if selectExpr, ok := starExp.X.(*ast.SelectorExpr); ok { if selectExpr.Sel.Name == testMethodStruct { if s, ok := selectExpr.X.(*ast.Ident); ok { - return s.Name == testMethodPackageType, param.Names[0].Name + if len(param.Names) > 0 { + return s.Name == testMethodPackageType, param.Names[0].Name + } } } } diff --git a/vendor/github.com/ldez/tagliatelle/readme.md b/vendor/github.com/ldez/tagliatelle/readme.md index 7bd728dd..55a544db 100644 --- a/vendor/github.com/ldez/tagliatelle/readme.md +++ b/vendor/github.com/ldez/tagliatelle/readme.md @@ -11,6 +11,7 @@ Supported string casing: - `pascal` - `kebab` - `snake` +- `upperSnake` - `goCamel` Respects [Go's common initialisms](https://github.com/golang/lint/blob/83fdc39ff7b56453e3793356bcff3070b9b96445/lint.go#L770-L809) (e.g. HttpResponse -> HTTPResponse). - `goPascal` Respects [Go's common initialisms](https://github.com/golang/lint/blob/83fdc39ff7b56453e3793356bcff3070b9b96445/lint.go#L770-L809) (e.g. HttpResponse -> HTTPResponse). - `goKebab` Respects [Go's common initialisms](https://github.com/golang/lint/blob/83fdc39ff7b56453e3793356bcff3070b9b96445/lint.go#L770-L809) (e.g. HttpResponse -> HTTPResponse). @@ -45,18 +46,18 @@ Supported string casing: | NameJSON | NameJson | NameJSON | | UneTête | UneTête | UneTête | -| Source | Snake Case | Go Snake Case | -|----------------|------------------|------------------| -| GooID | goo_id | goo_ID | -| HTTPStatusCode | http_status_code | HTTP_status_code | -| FooBAR | foo_bar | foo_bar | -| URL | url | URL | -| ID | id | ID | -| hostIP | host_ip | host_IP | -| JSON | json | JSON | -| JSONName | json_name | JSON_name | -| NameJSON | name_json | name_JSON | -| UneTête | une_tête | une_tête | +| Source | Snake Case | Upper Snake Case | Go Snake Case | +|----------------|------------------|------------------|------------------| +| GooID | goo_id | GOO_ID | goo_ID | +| HTTPStatusCode | http_status_code | HTTP_STATUS_CODE | HTTP_status_code | +| FooBAR | foo_bar | FOO_BAR | foo_bar | +| URL | url | URL | URL | +| ID | id | ID | ID | +| hostIP | host_ip | HOST_IP | host_IP | +| JSON | json | JSON | JSON | +| JSONName | json_name | JSON_NAME | JSON_name | +| NameJSON | name_json | NAME_JSON | name_JSON | +| UneTête | une_tête | UNE_TÊTE | une_tête | | Source | Kebab Case | Go KebabCase | |----------------|------------------|------------------| @@ -120,7 +121,7 @@ linters-settings: use-field-name: true rules: # Any struct tag type can be used. - # Support string case: `camel`, `pascal`, `kebab`, `snake`, `goCamel`, `goPascal`, `goKebab`, `goSnake`, `upper`, `lower` + # Support string case: `camel`, `pascal`, `kebab`, `snake`, `upperSnake`, `goCamel`, `goPascal`, `goKebab`, `goSnake`, `upper`, `lower`, `header`. json: camel yaml: camel xml: camel @@ -148,6 +149,7 @@ Here are the default rules for the well known and used tags, when using tagliate - `bson`: `camel` - `avro`: `snake` - `header`: `header` +- `envconfig`: `upperSnake` ### Custom Rules diff --git a/vendor/github.com/ldez/tagliatelle/tagliatelle.go b/vendor/github.com/ldez/tagliatelle/tagliatelle.go index c4653763..22c5feb3 100644 --- a/vendor/github.com/ldez/tagliatelle/tagliatelle.go +++ b/vendor/github.com/ldez/tagliatelle/tagliatelle.go @@ -204,6 +204,8 @@ func getConverter(c string) (func(s string) string, error) { return toHeader, nil case "upper": return strings.ToUpper, nil + case "upperSnake": + return strcase.ToSNAKE, nil case "lower": return strings.ToLower, nil default: diff --git a/vendor/github.com/mgechev/revive/config/config.go b/vendor/github.com/mgechev/revive/config/config.go index 4fbab0e3..04cd2140 100644 --- a/vendor/github.com/mgechev/revive/config/config.go +++ b/vendor/github.com/mgechev/revive/config/config.go @@ -3,7 +3,7 @@ package config import ( "errors" "fmt" - "io/ioutil" + "os" "github.com/mgechev/revive/formatter" @@ -140,7 +140,7 @@ func GetLintingRules(config *lint.Config, extraRules []lint.Rule) ([]lint.Rule, } func parseConfig(path string, config *lint.Config) error { - file, err := ioutil.ReadFile(path) + file, err := os.ReadFile(path) if err != nil { return errors.New("cannot read the config file") } diff --git a/vendor/github.com/mgechev/revive/rule/comment-spacings.go b/vendor/github.com/mgechev/revive/rule/comment-spacings.go index e0898c35..0d75c55f 100644 --- a/vendor/github.com/mgechev/revive/rule/comment-spacings.go +++ b/vendor/github.com/mgechev/revive/rule/comment-spacings.go @@ -23,6 +23,7 @@ func (r *CommentSpacingsRule) configure(arguments lint.Arguments) { r.allowList = []string{ "//go:", "//revive:", + "//nolint:", } for _, arg := range arguments { diff --git a/vendor/github.com/mgechev/revive/rule/empty-block.go b/vendor/github.com/mgechev/revive/rule/empty-block.go index 17652555..25a052a0 100644 --- a/vendor/github.com/mgechev/revive/rule/empty-block.go +++ b/vendor/github.com/mgechev/revive/rule/empty-block.go @@ -43,6 +43,13 @@ func (w lintEmptyBlock) Visit(node ast.Node) ast.Visitor { case *ast.SelectStmt: w.ignore[n.Body] = true return w + case *ast.ForStmt: + if len(n.Body.List) == 0 && n.Init == nil && n.Post == nil && n.Cond != nil { + if _, isCall := n.Cond.(*ast.CallExpr); isCall { + w.ignore[n.Body] = true + return w + } + } case *ast.RangeStmt: if len(n.Body.List) == 0 { w.onFailure(lint.Failure{ diff --git a/vendor/github.com/mgechev/revive/rule/nested-structs.go b/vendor/github.com/mgechev/revive/rule/nested-structs.go index fd122699..147bd482 100644 --- a/vendor/github.com/mgechev/revive/rule/nested-structs.go +++ b/vendor/github.com/mgechev/revive/rule/nested-structs.go @@ -14,7 +14,6 @@ func (*NestedStructs) Apply(file *lint.File, _ lint.Arguments) []lint.Failure { var failures []lint.Failure walker := &lintNestedStructs{ - fileAST: file.AST, onFailure: func(failure lint.Failure) { failures = append(failures, failure) }, @@ -31,47 +30,46 @@ func (*NestedStructs) Name() string { } type lintNestedStructs struct { - fileAST *ast.File onFailure func(lint.Failure) } func (l *lintNestedStructs) Visit(n ast.Node) ast.Visitor { - switch v := n.(type) { - case *ast.TypeSpec: - _, isInterface := v.Type.(*ast.InterfaceType) - if isInterface { - return nil // do not analyze interface declarations - } - case *ast.FuncDecl: - if v.Body != nil { - ast.Walk(l, v.Body) - } - return nil - case *ast.Field: - _, isChannelField := v.Type.(*ast.ChanType) - if isChannelField { - return nil - } + if v, ok := n.(*ast.StructType); ok { + ls := &lintStruct{l.onFailure} + ast.Walk(ls, v.Fields) + } - filter := func(n ast.Node) bool { - switch n.(type) { - case *ast.StructType: - return true - default: - return false - } - } - structs := pick(v, filter, nil) - for _, s := range structs { - l.onFailure(lint.Failure{ - Failure: "no nested structs are allowed", - Category: "style", - Node: s, - Confidence: 1, - }) + return l +} + +type lintStruct struct { + onFailure func(lint.Failure) +} + +func (l *lintStruct) Visit(n ast.Node) ast.Visitor { + switch s := n.(type) { + case *ast.StructType: + l.fail(s) + return nil + case *ast.ArrayType: + if _, ok := s.Elt.(*ast.StructType); ok { + l.fail(s) } - return nil // no need to visit (again) the field + return nil + case *ast.ChanType: + return nil + case *ast.MapType: + return nil + default: + return l } +} - return l +func (l *lintStruct) fail(n ast.Node) { + l.onFailure(lint.Failure{ + Failure: "no nested structs are allowed", + Category: "style", + Node: n, + Confidence: 1, + }) } diff --git a/vendor/github.com/mgechev/revive/rule/struct-tag.go b/vendor/github.com/mgechev/revive/rule/struct-tag.go index 45308d3b..d1c8056a 100644 --- a/vendor/github.com/mgechev/revive/rule/struct-tag.go +++ b/vendor/github.com/mgechev/revive/rule/struct-tag.go @@ -137,7 +137,7 @@ func (lintStructTagRule) getTagName(tag *structtag.Tag) string { case keyProtobuf: for _, option := range tag.Options { if strings.HasPrefix(option, "name=") { - return strings.TrimLeft(option, "name=") + return strings.TrimPrefix(option, "name=") } } return "" //protobuf tag lacks 'name' option diff --git a/vendor/github.com/nishanths/exhaustive/README.md b/vendor/github.com/nishanths/exhaustive/README.md index 0f16d81e..3cbc6cb3 100644 --- a/vendor/github.com/nishanths/exhaustive/README.md +++ b/vendor/github.com/nishanths/exhaustive/README.md @@ -1,18 +1,17 @@ -# exhaustive [![Godoc][godoc-svg]][godoc] +# exhaustive -Package exhaustive defines an analyzer that checks exhaustiveness of switch -statements of enum-like constants in Go source code. +[![Godoc][godoc-svg]][godoc] -For supported flags, the definition of enum, and the definition of -exhaustiveness used by this package, see [pkg.go.dev][godoc-doc]. For a -changelog, see [CHANGELOG][changelog] in the GitHub wiki. +`exhaustive` checks exhaustiveness of enum switch statements in Go source code. -The analyzer can be configured to additionally check exhaustiveness of map -literals whose key type is enum-like. +For the definition of enum and the definition of exhaustiveness used by this +program, see [godoc][godoc-doc]. For the changelog, see [CHANGELOG][changelog] +in the GitHub wiki. The program can be configured to additionally check +exhaustiveness of keys in map literals whose key type is an enum. ## Usage -Command line program: +Command: ``` go install github.com/nishanths/exhaustive/cmd/exhaustive@latest @@ -20,21 +19,26 @@ go install github.com/nishanths/exhaustive/cmd/exhaustive@latest exhaustive [flags] [packages] ``` +For available flags, refer to the [Flags][godoc-flags] section in godoc or run +`exhaustive -h`. + Package: ``` go get github.com/nishanths/exhaustive + +import "github.com/nishanths/exhaustive" ``` -The `exhaustive.Analyzer` variable follows the guidelines of the +The `exhaustive.Analyzer` variable follows guidelines in the [`golang.org/x/tools/go/analysis`][xanalysis] package. This should make it -possible to integrate `exhaustive` in your own analysis driver program. +possible to integrate `exhaustive` with your own analysis driver program. ## Example Given an enum: -```go +``` package token // import "example.org/token" type Token int @@ -48,49 +52,49 @@ const ( ) ``` -And code that switches on the enum: +and code that switches on the enum: -```go -package calc // import "example.org/calc" +``` +package calc import "example.org/token" -func f(t token.Token) { +func x(t token.Token) { switch t { case token.Add: case token.Subtract: - case token.Multiply: + case token.Remainder: default: } } - -var m = map[token.Token]string{ - token.Add: "add", - token.Subtract: "subtract", - token.Multiply: "multiply", -} ``` -Running `exhaustive` with default options will report: +running `exhaustive` with default options will produce: ``` -% exhaustive example.org/calc -calc.go:6:2: missing cases in switch of type token.Token: token.Quotient, token.Remainder +calc.go:6:2: missing cases in switch of type token.Token: token.Multiply, token.Quotient ``` -Specify the flag `-check=switch,map` to additionally check exhaustiveness of -map literal keys: +Specify flag `-check=switch,map` to additionally check exhaustiveness of keys +in map literals. For example: + +``` +var m = map[token.Token]rune{ + token.Add: '+', + token.Subtract: '-', + token.Multiply: '*', + token.Quotient: '/', +} +``` ``` -% exhaustive -check=switch,map example.org/calc -calc.go:6:2: missing cases in switch of type token.Token: token.Quotient, token.Remainder -calc.go:14:9: missing keys in map of key type token.Token: token.Quotient, token.Remainder +calc.go:14:9: missing keys in map of key type token.Token: token.Remainder ``` ## Contributing -Issues and changes are welcome. Please discuss substantial changes -in an issue first. +Issues and changes are welcome. Please discuss substantial changes in an issue +first. [godoc]: https://pkg.go.dev/github.com/nishanths/exhaustive [godoc-svg]: https://pkg.go.dev/badge/github.com/nishanths/exhaustive.svg @@ -98,4 +102,3 @@ in an issue first. [godoc-flags]: https://pkg.go.dev/github.com/nishanths/exhaustive#hdr-Flags [xanalysis]: https://pkg.go.dev/golang.org/x/tools/go/analysis [changelog]: https://github.com/nishanths/exhaustive/wiki/CHANGELOG -[issue-typeparam]: https://github.com/nishanths/exhaustive/issues/31 diff --git a/vendor/github.com/nishanths/exhaustive/comment.go b/vendor/github.com/nishanths/exhaustive/comment.go index 69b6e543..cc84beaf 100644 --- a/vendor/github.com/nishanths/exhaustive/comment.go +++ b/vendor/github.com/nishanths/exhaustive/comment.go @@ -44,7 +44,7 @@ const ( enforceComment = "//exhaustive:enforce" ) -func hasComment(comments []*ast.CommentGroup, comment string) bool { +func hasCommentPrefix(comments []*ast.CommentGroup, comment string) bool { for _, c := range comments { for _, cc := range c.List { if strings.HasPrefix(cc.Text, comment) { diff --git a/vendor/github.com/nishanths/exhaustive/common.go b/vendor/github.com/nishanths/exhaustive/common.go index 20fcf040..f598ec9f 100644 --- a/vendor/github.com/nishanths/exhaustive/common.go +++ b/vendor/github.com/nishanths/exhaustive/common.go @@ -227,7 +227,7 @@ func (c *checklist) remaining() map[member]struct{} { // different enum types. type group []member -func groupMissing(missing map[member]struct{}, types []enumType) []group { +func groupify(items map[member]struct{}, types []enumType) []group { // indices maps each element in the input slice to its index. indices := func(vs []enumType) map[enumType]int { ret := make(map[enumType]int, len(vs)) @@ -249,17 +249,17 @@ func groupMissing(missing map[member]struct{}, types []enumType) []group { } // byConstVal groups member names by constant value. - byConstVal := func(members map[member]struct{}) map[constantValue][]member { + byConstVal := func(items map[member]struct{}) map[constantValue][]member { ret := make(map[constantValue][]member) - for m := range members { + for m := range items { ret[m.val] = append(ret[m.val], m) } return ret } var groups []group - for _, members := range byConstVal(missing) { - groups = append(groups, group(members)) + for _, ms := range byConstVal(items) { + groups = append(groups, group(ms)) } // sort members within each group in AST order. @@ -319,8 +319,6 @@ func toEnumTypes(es []typeAndMembers) []enumType { } func dedupEnumTypes(types []enumType) []enumType { - // TODO(nishanths) this function is a candidate to use generics. - m := make(map[enumType]struct{}) var ret []enumType for _, t := range types { @@ -334,8 +332,8 @@ func dedupEnumTypes(types []enumType) []enumType { return ret } -// TODO(nishanths) If dropping pre-go1.18 support, the following -// types and functions are candidates to use generics. +// If dropping pre-go1.18 support, the following types and functions are +// candidates for generics. type boolCache struct { m map[*ast.File]bool diff --git a/vendor/github.com/nishanths/exhaustive/common_go118.go b/vendor/github.com/nishanths/exhaustive/common_go118.go index aebdd806..2614bb39 100644 --- a/vendor/github.com/nishanths/exhaustive/common_go118.go +++ b/vendor/github.com/nishanths/exhaustive/common_go118.go @@ -23,6 +23,7 @@ func fromNamed(pass *analysis.Pass, t *types.Named, typeparam bool) (result []ty } if typeparam { + // is it a named interface? if intf, ok := t.Underlying().(*types.Interface); ok { return fromInterface(pass, intf, typeparam) } @@ -31,34 +32,33 @@ func fromNamed(pass *analysis.Pass, t *types.Named, typeparam bool) (result []ty return nil, false // not a valid enum type, so ok == false } -func fromInterface(pass *analysis.Pass, intf *types.Interface, typeparam bool) (result []typeAndMembers, all bool) { - all = true - +func fromInterface(pass *analysis.Pass, intf *types.Interface, typeparam bool) (result []typeAndMembers, ok bool) { + allOk := true for i := 0; i < intf.NumEmbeddeds(); i++ { - embed := intf.EmbeddedType(i) - - switch embed.(type) { - case *types.Union: - u := embed.(*types.Union) - // gather from each term in the union. - for i := 0; i < u.Len(); i++ { - r, a := fromType(pass, u.Term(i).Type(), typeparam) - result = append(result, r...) - all = all && a - } - - case *types.Named: - r, a := fromNamed(pass, embed.(*types.Named), typeparam) - result = append(result, r...) - all = all && a + r, ok := fromType(pass, intf.EmbeddedType(i), typeparam) + result = append(result, r...) + allOk = allOk && ok + } + return result, allOk +} - default: - // don't care about these. - // e.g. basic type - } +func fromUnion(pass *analysis.Pass, union *types.Union, typeparam bool) (result []typeAndMembers, ok bool) { + allOk := true + // gather from each term in the union. + for i := 0; i < union.Len(); i++ { + r, ok := fromType(pass, union.Term(i).Type(), typeparam) + result = append(result, r...) + allOk = allOk && ok } + return result, allOk +} - return +func fromTypeParam(pass *analysis.Pass, tp *types.TypeParam, typeparam bool) (result []typeAndMembers, ok bool) { + // Does not appear to be explicitly documented, but based on Go language + // spec (see section Type constraints) and Go standard library source code, + // we can expect constraints to have underlying type *types.Interface + // Regardless it will be handled in fromType. + return fromType(pass, tp.Constraint().Underlying(), typeparam) } func fromType(pass *analysis.Pass, t types.Type, typeparam bool) (result []typeAndMembers, ok bool) { @@ -66,19 +66,18 @@ func fromType(pass *analysis.Pass, t types.Type, typeparam bool) (result []typeA case *types.Named: return fromNamed(pass, t, typeparam) + case *types.Union: + return fromUnion(pass, t, typeparam) + case *types.TypeParam: - // does not appear to be explicitly documented, but based on - // spec (see section Type constraints) and source code, we can - // expect constraints to have underlying type *types.Interface. - intf := t.Constraint().Underlying().(*types.Interface) - return fromInterface(pass, intf, typeparam) + return fromTypeParam(pass, t, typeparam) case *types.Interface: - // anonymous interface. - // e.g. func foo[T interface { M } | interface { N }](v T) {} if !typeparam { return nil, true } + // anonymous interface. + // e.g. func foo[T interface { M } | interface { N }](v T) {} return fromInterface(pass, t, typeparam) default: @@ -95,7 +94,7 @@ func composingEnumTypes(pass *analysis.Pass, t types.Type) (result []typeAndMemb var kind types.BasicKind var kindSet bool - // sameKind reports whether each type t that the function is called + // sameBasicKind reports whether each type t that the function is called // with has the same underlying basic kind. sameBasicKind := func(t types.Type) (ok bool) { basic, ok := t.Underlying().(*types.Basic) diff --git a/vendor/github.com/nishanths/exhaustive/doc.go b/vendor/github.com/nishanths/exhaustive/doc.go new file mode 100644 index 00000000..a6771af5 --- /dev/null +++ b/vendor/github.com/nishanths/exhaustive/doc.go @@ -0,0 +1,210 @@ +/* +Package exhaustive defines an analyzer that checks exhaustiveness of switch +statements of enum-like constants in Go source code. The analyzer can also +optionally check exhaustiveness of keys in map literals whose key type is +enum-like. + +# Definition of enum + +The Go [language spec] does not have an explicit definition for enums. For +the purpose of this analyzer, and by convention, an enum type is any named +type that: + + - has underlying type float, string, or integer (includes byte and rune); + and + - has at least one constant of its type defined in the same [block]. + +In the example below, Biome is an enum type. The three constants are its +enum members. + + package eco + + type Biome int + + const ( + Tundra Biome = 1 + Savanna Biome = 2 + Desert Biome = 3 + ) + +Enum member constants for an enum type must be declared in the same block as +the type. The constant values may be specified using iota, literal values, or +any valid means for declaring a Go constant. It is allowed for multiple enum +member constants for an enum type to have the same constant value. + +# Definition of exhaustiveness + +A switch statement that switches on a value of an enum type is exhaustive if +all enum members are listed in the switch statement's cases. If multiple enum +members have the same constant value, it is sufficient for any one of these +same-valued members to be listed. + +For an enum type defined in the same package as the switch statement, both +exported and unexported enum members must be listed to satisfy exhaustiveness. +For an enum type defined in an external package, it is sufficient that only +exported enum members are listed. Only constant identifiers (e.g. Tundra, +eco.Desert) listed in a switch statement's case clause can contribute towards +satisfying exhaustiveness; other expressions, such as literal values and +function calls, listed in case clauses do not contribute towards satisfying +exhaustiveness. + +By default, the existence of a default case in a switch statement does not +unconditionally make a switch statement exhaustive. Use the +-default-signifies-exhaustive flag to adjust this behavior. + +For a map literal whose key type is an enum type, a similar definition of +exhaustiveness applies. The map literal is considered exhaustive if all enum +members are be listed in its keys. Empty map literals are never checked for +exhaustiveness. + +# Type parameters + +A switch statement that switches on a value whose type is a type parameter is +checked for exhaustiveness if and only if each type element in the type +constraint is an enum type and the type elements share the same underlying +[BasicKind]. + +For example, the switch statement below will be checked because each type +element (i.e. M and N) in the type constraint is an enum type and the type +elements share the same underlying BasicKind, namely int8. To satisfy +exhaustiveness, the enum members collectively belonging to the enum types M +and N (i.e. A, B, and C) must be listed in the switch statement's cases. + + func bar[T M | I](v T) { + switch v { + case T(A): + case T(B): + case T(C): + } + } + + type I interface{ N } + + type M int8 + const A M = 1 + + type N int8 + const B N = 2 + const C N = 3 + +# Type aliases + +The analyzer handles type aliases as shown in the example below. newpkg.M is +an enum type. oldpkg.M is an alias for newpkg.M. Note that oldpkg.M isn't +itself an enum type; oldpkg.M is simply an alias for the actual enum type +newpkg.M. + + package oldpkg + type M = newpkg.M + const ( + A = newpkg.A + B = newpkg.B + ) + + package newpkg + type M int + const ( + A M = 1 + B M = 2 + ) + +A switch statement that switches either on a value of type newpkg.M or of type +oldpkg.M (which, being an alias, is just an alternative spelling for newpkg.M) +is exhaustive if all of newpkg.M's enum members are listed in the switch +statement's cases. The following switch statement is exhaustive. + + func f(v newpkg.M) { + switch v { + case newpkg.A: // or equivalently oldpkg.A + case newpkg.B: // or equivalently oldpkg.B + } + } + +The analyzer guarantees that introducing a type alias (such as type M = +newpkg.M) will not result in new diagnostics if the set of enum member +constant values of the RHS type is a subset of the set of enum member constant +values of the LHS type. + +# Flags + +Summary: + + flag type default value + ---- ---- ------------- + -check comma-separated strings switch + -explicit-exhaustive-switch bool false + -explicit-exhaustive-map bool false + -check-generated bool false + -default-signifies-exhaustive bool false + -ignore-enum-members regexp pattern (none) + -ignore-enum-types regexp pattern (none) + -package-scope-only bool false + +Descriptions: + + -check + Comma-separated list of program elements to check for exhaustiveness. + Supported program element values are "switch" and "map". The default + value is "switch", which means that only switch statements are checked. + + -explicit-exhaustive-switch + Check a switch statement only if it is associated with a + "//exhaustive:enforce" comment. By default the analyzer checks every + switch statement that isn't associated with a "//exhaustive:ignore" + comment. + + -explicit-exhaustive-map + Similar to -explicit-exhaustive-switch but for map literals. + + -check-generated + Check generated files. For the definition of a generated file, see + https://golang.org/s/generatedcode. + + -default-signifies-exhaustive + Consider a switch statement to be exhaustive unconditionally if it has a + default case. (In other words, all enum members do not have to be listed + in its cases if a default case is present.) Setting this flag usually is + counter to the purpose of exhaustiveness checks, so it is not + recommended to set this flag. + + -ignore-enum-members + Constants that match the specified regular expression (in package regexp + syntax) are not considered enum members and hence do not have to be + listed to satisfy exhaustiveness. The specified regular expression is + matched against the constant name inclusive of import path. For example, + if the import path for the constant is "example.org/eco" and the + constant name is "Tundra", then the specified regular expression is + matched against the string "example.org/eco.Tundra". + + -ignore-enum-types + Similar to -ignore-enum-members but for types. + + -package-scope-only + Only discover enums declared in file-level blocks. By default, the + analyzer discovers enums defined in all blocks. + +# Skip analysis + +To skip analysis of a switch statement or a map literal, associate it with a +comment that begins with "//exhaustive:ignore". For example: + + //exhaustive:ignore ... an optional explanation goes here ... + switch v { + case A: + case B: + } + +To ignore specific constants in exhaustiveness checks, specify the +-ignore-enum-members flag: + + exhaustive -ignore-enum-members '^example\.org/eco\.Tundra$' + +To ignore specific types, specify the -ignore-enum-types flag: + + exhaustive -ignore-enum-types '^time\.Duration$|^example\.org/measure\.Unit$' + +[language spec]: https://golang.org/ref/spec +[block]: https://golang.org/ref/spec#Blocks +[BasicKind]: https://pkg.go.dev/go/types#BasicKind +*/ +package exhaustive diff --git a/vendor/github.com/nishanths/exhaustive/enum.go b/vendor/github.com/nishanths/exhaustive/enum.go index fa46eb92..a1c41623 100644 --- a/vendor/github.com/nishanths/exhaustive/enum.go +++ b/vendor/github.com/nishanths/exhaustive/enum.go @@ -25,7 +25,7 @@ func (et enumType) factObject() types.Object { return et.TypeName } // // The zero value is ready to use. type enumMembers struct { Names []string // enum member names - NameToPos map[string]token.Pos // member name -> AST position + NameToPos map[string]token.Pos // enum member name -> AST position NameToValue map[string]constantValue // enum member name -> constant value ValueToNames map[constantValue][]string // constant value -> enum member names } diff --git a/vendor/github.com/nishanths/exhaustive/exhaustive.go b/vendor/github.com/nishanths/exhaustive/exhaustive.go index ddb9ee0a..d2a877d5 100644 --- a/vendor/github.com/nishanths/exhaustive/exhaustive.go +++ b/vendor/github.com/nishanths/exhaustive/exhaustive.go @@ -1,229 +1,3 @@ -/* -Package exhaustive defines an analyzer that checks exhaustiveness of switch -statements of enum-like constants in Go source code. The analyzer can be -configured to additionally check exhaustiveness of map literals whose key type -is enum-like. - -# Definition of enum - -The Go [language spec] does not provide an explicit definition for enums. For -the purpose of this analyzer, and by convention, an enum type is any named -type that has: - - - underlying type float, string, or integer (includes byte and - rune, which are aliases for uint8 and int32, respectively); and - - at least one constant of the type defined in the same scope. - -In the example below, Biome is an enum type. The three constants are its -enum members. - - package eco - - type Biome int - - const ( - Tundra Biome = 1 - Savanna Biome = 2 - Desert Biome = 3 - ) - -Enum member constants for a particular enum type do not necessarily all -have to be declared in the same const block. The constant values may be -specified using iota, using literal values, or using any valid means for -declaring a Go constant. It is allowed for multiple enum member -constants for a particular enum type to have the same constant value. - -# Definition of exhaustiveness - -A switch statement that switches on a value of an enum type is exhaustive if -all enum members, by constant value, are listed in the switch -statement's cases. If multiple members have the same constant value, it is -sufficient for any one of these same-valued members to be listed. - -For an enum type defined in the same package as the switch statement, both -exported and unexported enum members must be listed to satisfy exhaustiveness. -For an enum type defined in an external package, it is sufficient that only -exported enum members are listed. In a switch statement's cases, only -identifiers (e.g. Tundra) and qualified identifiers (e.g. somepkg.Grassland) -that name constants may contribute towards satisfying exhaustiveness; other -expressions such as literal values and function calls will not. - -By default, the existence of a default case in a switch statement does not -unconditionally make a switch statement exhaustive. Use the --default-signifies-exhaustive flag to adjust this behavior. - -A similar definition of exhaustiveness applies to a map literal whose key type -is an enum type. For the map literal to be considered exhaustive, all enum -members, by constant value, must be listed as keys. Empty map literals are not -checked. For the analyzer to check map literals, the -check flag must include -the value "map". - -# Type parameters - -A switch statement that switches on a value whose type is a type parameter is -checked for exhaustiveness if each type element in the type constraint is an -enum type and shares the same underlying basic type kind. - -In the following example, the switch statement on the value of type parameter -T will be checked, because each type element of T—namely M, N, and O—is an -enum type and shares the same underlying basic type kind (i.e. int8). To -satisfy exhaustiveness, all enum members, by constant value, for each of the -enum types M, N, and O—namely A, B, C, and D—must be listed in the switch -statement's cases. - - func bar[T M | I](v T) { - switch v { - case T(A): - case T(B): - case T(C): - case T(D): - } - } - - type I interface{ N | J } - type J interface{ O } - - type M int8 - const A M = 1 - - type N int8 - const B N = 2 - const C N = 3 - - type O int8 - const D O = 4 - -# Type aliases - -The analyzer handles type aliases as shown in the example below. Here T2 is a -enum type. T1 is an alias for T2. Note that T1 itself isn't considered an enum -type; T1 is only an alias for an enum type. - - package pkg - type T1 = newpkg.T2 - const ( - A = newpkg.A - B = newpkg.B - ) - - package newpkg - type T2 int - const ( - A T2 = 1 - B T2 = 2 - ) - -A switch statement that switches on a value of type T1 (which, in reality, is -just an alternate spelling for type T2) is exhaustive if all of T2's enum -members, by constant value, are listed in the switch statement's cases. -(Recall that only constants declared in the same scope as type T2's scope can -be T2's enum members.) - -The following switch statements are exhaustive. - - // Note: the type of v is effectively newpkg.T2, due to type aliasing. - func f(v pkg.T1) { - switch v { - case newpkg.A: - case newpkg.B: - } - } - - func g(v pkg.T1) { - switch v { - case pkg.A: - case pkg.B: - } - } - -The analyzer guarantees that introducing a type alias (such as type T1 = -newpkg.T2) will not result in new diagnostics from the analyzer, as long as -the set of enum member constant values of the alias RHS type is a subset of -the set of enum member constant values of the LHS type. - -# Flags - -Summary: - - flag type default value - ---- ---- ------------- - -check comma-separated string switch - -explicit-exhaustive-switch bool false - -explicit-exhaustive-map bool false - -check-generated bool false - -default-signifies-exhaustive bool false - -ignore-enum-members regexp pattern (none) - -ignore-enum-types regexp pattern (none) - -package-scope-only bool false - -Flag descriptions: - - - The -check flag specifies a comma-separated list of program elements - that should be checked for exhaustiveness; supported program elements - are "switch" and "map". The default flag value is "switch", which means - that only switch statements are checked. Specify the flag value - "switch,map" to check both switch statements and map literals. - - - If -explicit-exhaustive-switch is enabled, the analyzer checks a switch - statement only if it is associated with a comment beginning with - "//exhaustive:enforce". Otherwise, the analyzer checks every enum switch - statement not associated with a comment beginning with - "//exhaustive:ignore". - - - The -explicit-exhaustive-map flag is the map literal counterpart for the - -explicit-exhaustive-switch flag. - - - If -check-generated is enabled, switch statements and map literals in - generated Go source files are checked. By default, the analyzer does not - check generated files. Refer to https://golang.org/s/generatedcode for - the definition of generated files. - - - If -default-signifies-exhaustive is enabled, the presence of a default - case in a switch statement unconditionally satisfies exhaustiveness (all - enum members do not have to be listed). Enabling this flag usually tends - to counter the purpose of exhaustiveness checking, so it is not - recommended that you enable this flag. - - - The -ignore-enum-members flag specifies a regular expression in Go - package regexp syntax. Constants matching the regular expression do not - have to be listed in switch statement cases or map literals in order to - satisfy exhaustiveness. The specified regular expression is matched - against the constant name inclusive of the enum package import path. For - example, if the package import path of the constant is "example.org/eco" - and the constant name is "Tundra", the specified regular expression will - be matched against the string "example.org/eco.Tundra". - - - The -ignore-enum-types flag is similar to the -ignore-enum-members flag, - except that it applies to types. - - - If -package-scope-only is enabled, the analyzer only finds enums defined - in package scope but not in inner scopes such as functions; consequently - only switch statements and map literals that use such enums are checked - for exhaustiveness. By default, the analyzer finds enums defined in all - scopes, including in inner scopes such as functions. - -# Skip analysis - -To skip analysis of a switch statement or a map literal, associate it with a -comment that begins with "//exhaustive:ignore". For example: - - //exhaustive:ignore - switch v { - case A: - case B: - } - -To ignore specific constants in exhaustiveness checks, use the --ignore-enum-members flag: - - exhaustive -ignore-enum-members '^example\.org/eco\.Tundra$' - -To ignore specific types, use the -ignore-enum-types flag: - - exhaustive -ignore-enum-types '^time\.Duration$|^example\.org/measure\.Unit$' - -[language spec]: https://golang.org/ref/spec -*/ package exhaustive import ( @@ -236,21 +10,25 @@ import ( ) func init() { - Analyzer.Flags.Var(&fCheck, CheckFlag, "comma-separated list of program `elements` that should be checked for exhaustiveness; supported elements are: switch, map") + registerFlags() +} + +func registerFlags() { + Analyzer.Flags.Var(&fCheck, CheckFlag, "comma-separated list of program `elements` to check for exhaustiveness; supported element values: switch, map") Analyzer.Flags.BoolVar(&fExplicitExhaustiveSwitch, ExplicitExhaustiveSwitchFlag, false, `check switch statement only if associated with "//exhaustive:enforce" comment`) Analyzer.Flags.BoolVar(&fExplicitExhaustiveMap, ExplicitExhaustiveMapFlag, false, `check map literal only if associated with "//exhaustive:enforce" comment`) Analyzer.Flags.BoolVar(&fCheckGenerated, CheckGeneratedFlag, false, "check generated files") - Analyzer.Flags.BoolVar(&fDefaultSignifiesExhaustive, DefaultSignifiesExhaustiveFlag, false, "presence of default case in switch statement unconditionally satisfies exhaustiveness") - Analyzer.Flags.Var(&fIgnoreEnumMembers, IgnoreEnumMembersFlag, "constants matching `regexp` are ignored for exhaustiveness checks") - Analyzer.Flags.Var(&fIgnoreEnumTypes, IgnoreEnumTypesFlag, "types matching `regexp` are ignored for exhaustiveness checks") - Analyzer.Flags.BoolVar(&fPackageScopeOnly, PackageScopeOnlyFlag, false, "find enums only in package scopes, not inner scopes") + Analyzer.Flags.BoolVar(&fDefaultSignifiesExhaustive, DefaultSignifiesExhaustiveFlag, false, "switch statement is unconditionally exhaustive if it has a default case") + Analyzer.Flags.Var(&fIgnoreEnumMembers, IgnoreEnumMembersFlag, "ignore constants matching `regexp`") + Analyzer.Flags.Var(&fIgnoreEnumTypes, IgnoreEnumTypesFlag, "ignore types matching `regexp`") + Analyzer.Flags.BoolVar(&fPackageScopeOnly, PackageScopeOnlyFlag, false, "only discover enums declared in file-level blocks") var unused string Analyzer.Flags.StringVar(&unused, IgnorePatternFlag, "", "no effect (deprecated); use -"+IgnoreEnumMembersFlag) Analyzer.Flags.StringVar(&unused, CheckingStrategyFlag, "", "no effect (deprecated)") } -// Flag names used by the analyzer. They are exported for use by analyzer +// Flag names used by the analyzer. These are exported for use by analyzer // driver programs. const ( CheckFlag = "check" @@ -262,33 +40,11 @@ const ( IgnoreEnumTypesFlag = "ignore-enum-types" PackageScopeOnlyFlag = "package-scope-only" + // Deprecated flag names. IgnorePatternFlag = "ignore-pattern" // Deprecated: use IgnoreEnumMembersFlag. - CheckingStrategyFlag = "checking-strategy" // Deprecated. -) - -// checkElement is a program element supported by the -check flag. -type checkElement string - -const ( - elementSwitch checkElement = "switch" - elementMap checkElement = "map" + CheckingStrategyFlag = "checking-strategy" // Deprecated: no longer applicable. ) -func validCheckElement(s string) error { - switch checkElement(s) { - case elementSwitch: - return nil - case elementMap: - return nil - default: - return fmt.Errorf("invalid program element %q", s) - } -} - -var defaultCheckElements = []string{ - string(elementSwitch), -} - // Flag values. var ( fCheck = stringsFlag{elements: defaultCheckElements, filter: validCheckElement} @@ -301,7 +57,7 @@ var ( fPackageScopeOnly bool ) -// resetFlags resets the flag variables to their default values. +// resetFlags resets the flag variables to default values. // Useful in tests. func resetFlags() { fCheck = stringsFlag{elements: defaultCheckElements, filter: validCheckElement} @@ -314,6 +70,29 @@ func resetFlags() { fPackageScopeOnly = false } +// checkElement is a program element supported by the -check flag. +type checkElement string + +const ( + elementSwitch checkElement = "switch" + elementMap checkElement = "map" +) + +func validCheckElement(s string) error { + switch checkElement(s) { + case elementSwitch: + return nil + case elementMap: + return nil + default: + return fmt.Errorf("invalid program element %q", s) + } +} + +var defaultCheckElements = []string{ + string(elementSwitch), +} + var Analyzer = &analysis.Analyzer{ Name: "exhaustive", Doc: "check exhaustiveness of enum switch statements", @@ -325,32 +104,12 @@ var Analyzer = &analysis.Analyzer{ func run(pass *analysis.Pass) (interface{}, error) { inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) - for typ, members := range findEnums( - fPackageScopeOnly, - pass.Pkg, - inspect, - pass.TypesInfo, - ) { + for typ, members := range findEnums(fPackageScopeOnly, pass.Pkg, inspect, pass.TypesInfo) { exportFact(pass, typ, members) } generated := boolCache{value: isGeneratedFile} comments := commentCache{value: fileCommentMap} - swConf := switchConfig{ - explicit: fExplicitExhaustiveSwitch, - defaultSignifiesExhaustive: fDefaultSignifiesExhaustive, - checkGenerated: fCheckGenerated, - ignoreConstant: fIgnoreEnumMembers.re, - ignoreType: fIgnoreEnumTypes.re, - } - mapConf := mapConfig{ - explicit: fExplicitExhaustiveMap, - checkGenerated: fCheckGenerated, - ignoreConstant: fIgnoreEnumMembers.re, - ignoreType: fIgnoreEnumTypes.re, - } - swChecker := switchChecker(pass, swConf, generated, comments) - mapChecker := mapChecker(pass, mapConf, generated, comments) // NOTE: should not share the same inspect.WithStack call for different // program elements: the visitor function for a program element may @@ -359,12 +118,30 @@ func run(pass *analysis.Pass) (interface{}, error) { for _, e := range fCheck.elements { switch checkElement(e) { case elementSwitch: - inspect.WithStack([]ast.Node{&ast.SwitchStmt{}}, toVisitor(swChecker)) + conf := switchConfig{ + explicit: fExplicitExhaustiveSwitch, + defaultSignifiesExhaustive: fDefaultSignifiesExhaustive, + checkGenerated: fCheckGenerated, + ignoreConstant: fIgnoreEnumMembers.re, + ignoreType: fIgnoreEnumTypes.re, + } + checker := switchChecker(pass, conf, generated, comments) + inspect.WithStack([]ast.Node{&ast.SwitchStmt{}}, toVisitor(checker)) + case elementMap: - inspect.WithStack([]ast.Node{&ast.CompositeLit{}}, toVisitor(mapChecker)) + conf := mapConfig{ + explicit: fExplicitExhaustiveMap, + checkGenerated: fCheckGenerated, + ignoreConstant: fIgnoreEnumMembers.re, + ignoreType: fIgnoreEnumTypes.re, + } + checker := mapChecker(pass, conf, generated, comments) + inspect.WithStack([]ast.Node{&ast.CompositeLit{}}, toVisitor(checker)) + default: panic(fmt.Sprintf("unknown checkElement %v", e)) } } + return nil, nil } diff --git a/vendor/github.com/nishanths/exhaustive/map.go b/vendor/github.com/nishanths/exhaustive/map.go index 06923338..3626f45c 100644 --- a/vendor/github.com/nishanths/exhaustive/map.go +++ b/vendor/github.com/nishanths/exhaustive/map.go @@ -77,13 +77,13 @@ func mapChecker(pass *analysis.Pass, cfg mapConfig, generated boolCache, comment } } - if !cfg.explicit && hasComment(relatedComments, ignoreComment) { + if !cfg.explicit && hasCommentPrefix(relatedComments, ignoreComment) { // Skip checking of this map literal due to ignore // comment. Still return true because there may be nested // map literals that are not to be ignored. return true, resultIgnoreComment } - if cfg.explicit && !hasComment(relatedComments, enforceComment) { + if cfg.explicit && !hasCommentPrefix(relatedComments, enforceComment) { return true, resultNoEnforceComment } @@ -128,7 +128,7 @@ func makeMapDiagnostic(lit *ast.CompositeLit, enumTypes []enumType, missing map[ Message: fmt.Sprintf( "missing keys in map of key type %s: %s", diagnosticEnumTypes(enumTypes), - diagnosticGroups(groupMissing(missing, enumTypes)), + diagnosticGroups(groupify(missing, enumTypes)), ), } } diff --git a/vendor/github.com/nishanths/exhaustive/switch.go b/vendor/github.com/nishanths/exhaustive/switch.go index 2e99d283..57c44c19 100644 --- a/vendor/github.com/nishanths/exhaustive/switch.go +++ b/vendor/github.com/nishanths/exhaustive/switch.go @@ -80,13 +80,13 @@ func switchChecker(pass *analysis.Pass, cfg switchConfig, generated boolCache, c sw := n.(*ast.SwitchStmt) switchComments := comments.get(pass.Fset, file)[sw] - if !cfg.explicit && hasComment(switchComments, ignoreComment) { + if !cfg.explicit && hasCommentPrefix(switchComments, ignoreComment) { // Skip checking of this switch statement due to ignore // comment. Still return true because there may be nested // switch statements that are not to be ignored. return true, resultIgnoreComment } - if cfg.explicit && !hasComment(switchComments, enforceComment) { + if cfg.explicit && !hasCommentPrefix(switchComments, enforceComment) { // Skip checking of this switch statement due to missing // enforce comment. return true, resultNoEnforceComment @@ -163,7 +163,7 @@ func makeSwitchDiagnostic(sw *ast.SwitchStmt, enumTypes []enumType, missing map[ Message: fmt.Sprintf( "missing cases in switch of type %s: %s", diagnosticEnumTypes(enumTypes), - diagnosticGroups(groupMissing(missing, enumTypes)), + diagnosticGroups(groupify(missing, enumTypes)), ), } } diff --git a/vendor/github.com/nunnatsa/ginkgolinter/Makefile b/vendor/github.com/nunnatsa/ginkgolinter/Makefile index f3707983..e8efae58 100644 --- a/vendor/github.com/nunnatsa/ginkgolinter/Makefile +++ b/vendor/github.com/nunnatsa/ginkgolinter/Makefile @@ -1,14 +1,24 @@ +VERSION ?= "unknown" +VERSION_FLAG := -X github.com/nunnatsa/ginkgolinter/version.version=$(VERSION) +COMMIT_HASH := $(shell git rev-parse HEAD) +HASH_FLAG := -X github.com/nunnatsa/ginkgolinter/version.gitHash=$(COMMIT_HASH) + +BUILD_ARGS := -ldflags "$(VERSION_FLAG) $(HASH_FLAG)" + build: - go build -o ginkgolinter ./cmd/ginkgolinter + go build $(BUILD_ARGS) -o ginkgolinter ./cmd/ginkgolinter build-for-windows: - GOOS=windows GOARCH=amd64 go build -o bin/ginkgolinter-amd64.exe ./cmd/ginkgolinter + GOOS=windows GOARCH=amd64 go build $(BUILD_ARGS) -o bin/ginkgolinter-amd64.exe ./cmd/ginkgolinter build-for-mac: - GOOS=darwin GOARCH=amd64 go build -o bin/ginkgolinter-amd64-darwin ./cmd/ginkgolinter + GOOS=darwin GOARCH=amd64 go build $(BUILD_ARGS) -o bin/ginkgolinter-amd64-darwin ./cmd/ginkgolinter build-for-linux: - GOOS=linux GOARCH=amd64 go build -o bin/ginkgolinter-amd64-linux ./cmd/ginkgolinter - GOOS=linux GOARCH=386 go build -o bin/ginkgolinter-386-linux ./cmd/ginkgolinter + GOOS=linux GOARCH=amd64 go build $(BUILD_ARGS) -o bin/ginkgolinter-amd64-linux ./cmd/ginkgolinter + GOOS=linux GOARCH=386 go build $(BUILD_ARGS) -o bin/ginkgolinter-386-linux ./cmd/ginkgolinter build-all: build build-for-linux build-for-mac build-for-windows + +test: build + ./tests/e2e.sh diff --git a/vendor/github.com/nunnatsa/ginkgolinter/README.md b/vendor/github.com/nunnatsa/ginkgolinter/README.md index 296f223e..e0a4b073 100644 --- a/vendor/github.com/nunnatsa/ginkgolinter/README.md +++ b/vendor/github.com/nunnatsa/ginkgolinter/README.md @@ -198,12 +198,92 @@ Expect(x1 == c1).Should(BeTrue()) // ==> Expect(x1).Should(Equal(c1)) Expect(c1 == x1).Should(BeTrue()) // ==> Expect(x1).Should(Equal(c1)) ``` +### Using a function call in async assertion +This rule finds an actual bug in tests, where asserting a function call in an async function; e.g. `Eventually`. For +example: +```go +func slowInt(int val) int { + time.Sleep(time.Second) + return val +} + +... + +It("should test that slowInt returns 42, eventually", func() { + Eventually(slowInt(42)).WithPolling(time.Millisecond * 100).WithTimeout(time.Second * 2).Equal(42) +}) +``` +The problem with the above code is that it **should** poll - call the function - until it returns 42, but what actually +happens is that first the function is called, and we pass `42` to `Eventually` - not the function. This is not what we +tried to do here. + +The linter will suggest replacing this code by: +```go +It("should test that slowInt returns 42, eventually", func() { + Eventually(slowInt).WithArguments(42).WithPolling(time.Millisecond * 100).WithTimeout(time.Second * 2).Equal(42) +}) +``` + +The linter suggested replacing the function call by the function name. + +If function arguments are used, the linter will add the `WithArguments()` method to pass them. + +Please notice that `WithArguments()` is only supported from gomenga v1.22.0. + +When using an older version of gomega, change the code manually. For example: + +```go +It("should test that slowInt returns 42, eventually", func() { + Eventually(func() int { + slowint(42) + }).WithPolling(time.Millisecond * 100).WithTimeout(time.Second * 2).Equal(42) +}) +``` + +### Comparing a pointer with a value +The linter warns when comparing a pointer with a value. +These comparisons are always wrong and will always fail. + +In case of a positive assertion (`To()` or `Should()`), the test will just fail. + +But the main concern is for false positive tests, when using a negative assertion (`NotTo()`, `ToNot()`, `ShouldNot()`, +`Should(Not())` etc.); e.g. +```go +num := 5 +... +pNum := &num +... +Expect(pNum).ShouldNot(Equal(6)) +``` +This assertion will pass, but for the wrong reasons: pNum is not equal 6, not because num == 5, but because pNum is +a pointer, while `6` is an `int`. + +In the case above, the linter will suggest `Expect(pNum).ShouldNot(HaveValue(Equal(6)))` + +This is also right for additional matchers: `BeTrue()` and `BeFalse()`, `BeIdenticalTo()`, `BeEquivalentTo()` +and `BeNumerically`. + +### Missing Assertion Method +The linter warns when calling an "actual" method (e.g. `Expect()`, `Eventually()` etc.), without an assertion method (e.g +`Should()`, `NotTo()` etc.) + +For example: +```go +// no assertion for the result +Eventually(doSomething).WithTimeout(time.Seconds * 5).WithPolling(time.Milliseconds * 100) +``` + +The linter will not suggest a fix for this warning. + +This rule cannot be suppressed. + ## Suppress the linter ### Suppress warning from command line * Use the `--suppress-len-assertion=true` flag to suppress the wrong length assertion warning * Use the `--suppress-nil-assertion=true` flag to suppress the wrong nil assertion warning * Use the `--suppress-err-assertion=true` flag to suppress the wrong error assertion warning * Use the `--suppress-compare-assertion=true` flag to suppress the wrong comparison assertion warning +* Use the `--suppress-async-assertion=true` flag to suppress the function call in async assertion warning * Use the `--allow-havelen-0=true` flag to avoid warnings about `HaveLen(0)`; Note: this parameter is only supported from command line, and not from a comment. @@ -224,6 +304,10 @@ To suppress the wrong comparison assertion warning, add a comment with (only) `ginkgo-linter:ignore-compare-assert-warning`. +To suppress the wrong async assertion warning, add a comment with (only) + +`ginkgo-linter:ignore-async-assert-warning`. + There are two options to use these comments: 1. If the comment is at the top of the file, supress the warning for the whole file; e.g.: ```go diff --git a/vendor/github.com/nunnatsa/ginkgolinter/ginkgo_linter.go b/vendor/github.com/nunnatsa/ginkgolinter/ginkgo_linter.go index c04b694a..d9f437f0 100644 --- a/vendor/github.com/nunnatsa/ginkgolinter/ginkgo_linter.go +++ b/vendor/github.com/nunnatsa/ginkgolinter/ginkgo_linter.go @@ -4,12 +4,14 @@ import ( "bytes" "flag" "fmt" - "github.com/go-toolsmith/astcopy" + "github.com/nunnatsa/ginkgolinter/version" "go/ast" "go/constant" "go/printer" "go/token" gotypes "go/types" + + "github.com/go-toolsmith/astcopy" "golang.org/x/tools/go/analysis" "github.com/nunnatsa/ginkgolinter/gomegahandler" @@ -22,27 +24,43 @@ import ( // For more details, look at the README.md file const ( - linterName = "ginkgo-linter" - wrongLengthWarningTemplate = linterName + ": wrong length assertion; consider using `%s` instead" - wrongNilWarningTemplate = linterName + ": wrong nil assertion; consider using `%s` instead" - wrongBoolWarningTemplate = linterName + ": wrong boolean assertion; consider using `%s` instead" - wrongErrWarningTemplate = linterName + ": wrong error assertion; consider using `%s` instead" - wrongCompareWarningTemplate = linterName + ": wrong comparison assertion; consider using `%s` instead" - beEmpty = "BeEmpty" - beNil = "BeNil" - beTrue = "BeTrue" - beFalse = "BeFalse" - beZero = "BeZero" - beNumerically = "BeNumerically" - beIdenticalTo = "BeIdenticalTo" - equal = "Equal" - not = "Not" - haveLen = "HaveLen" - succeed = "Succeed" - haveOccurred = "HaveOccurred" - expect = "Expect" - omega = "Ω" - expectWithOffset = "ExpectWithOffset" + linterName = "ginkgo-linter" + wrongLengthWarningTemplate = linterName + ": wrong length assertion; consider using `%s` instead" + wrongNilWarningTemplate = linterName + ": wrong nil assertion; consider using `%s` instead" + wrongBoolWarningTemplate = linterName + ": wrong boolean assertion; consider using `%s` instead" + wrongErrWarningTemplate = linterName + ": wrong error assertion; consider using `%s` instead" + wrongCompareWarningTemplate = linterName + ": wrong comparison assertion; consider using `%s` instead" + doubleNegativeWarningTemplate = linterName + ": avoid double negative assertion; consider using `%s` instead" + valueInEventually = linterName + ": use a function call in %s. This actually checks nothing, because %s receives the function returned value, instead of function itself, and this value is never changed" + comparePointerToValue = linterName + ": comparing a pointer to a value will always fail. consider using `%s` instead" + missingAssertionMessage = linterName + `: %q: missing assertion method. Expected "Should()", "To()", "ShouldNot()", "ToNot()" or "NotTo()"` + missingAsyncAssertionMessage = linterName + `: %q: missing assertion method. Expected "Should()" or "ShouldNot()"` +) +const ( // gomega matchers + beEmpty = "BeEmpty" + beEquivalentTo = "BeEquivalentTo" + beFalse = "BeFalse" + beIdenticalTo = "BeIdenticalTo" + beNil = "BeNil" + beNumerically = "BeNumerically" + beTrue = "BeTrue" + beZero = "BeZero" + equal = "Equal" + haveLen = "HaveLen" + haveOccurred = "HaveOccurred" + haveValue = "HaveValue" + not = "Not" + omega = "Ω" + succeed = "Succeed" +) + +const ( // gomega actuals + expect = "Expect" + expectWithOffset = "ExpectWithOffset" + eventually = "Eventually" + eventuallyWithOffset = "EventuallyWithOffset" + consistently = "Consistently" + consistentlyWithOffset = "ConsistentlyWithOffset" ) // Analyzer is the interface to go_vet @@ -65,10 +83,9 @@ func NewAnalyzer() *analysis.Analyzer { } a := &analysis.Analyzer{ - Name: "ginkgolinter", - Doc: doc, - Run: linter.run, - RunDespiteErrors: true, + Name: "ginkgolinter", + Doc: fmt.Sprintf(doc, version.Version()), + Run: linter.run, } a.Flags.Init("ginkgolinter", flag.ExitOnError) @@ -76,56 +93,64 @@ func NewAnalyzer() *analysis.Analyzer { a.Flags.Var(&linter.config.SuppressNil, "suppress-nil-assertion", "Suppress warning for wrong nil assertions") a.Flags.Var(&linter.config.SuppressErr, "suppress-err-assertion", "Suppress warning for wrong error assertions") a.Flags.Var(&linter.config.SuppressCompare, "suppress-compare-assertion", "Suppress warning for wrong comparison assertions") + a.Flags.Var(&linter.config.SuppressAsync, "suppress-async-assertion", "Suppress warning for function call in async assertion, like Eventually") a.Flags.Var(&linter.config.AllowHaveLen0, "allow-havelen-0", "Do not warn for HaveLen(0); default = false") return a } const doc = `enforces standards of using ginkgo and gomega + +or + ginkgolinter version + +version: %s + currently, the linter searches for following: +* trigger a warning when using Eventually or Constantly with a function call. This is in order to prevent the case when + using a function call instead of a function. Function call returns a value only once, and so the original value + is tested again and again and is never changed. [Bug] -* wrong length assertions. We want to assert the item rather than its length. +* trigger a warning when comparing a pointer to a value. [Bug] + +* trigger a warning for missing assertion method: [Bug] + Eventually(checkSomething) + +* wrong length assertions. We want to assert the item rather than its length. [Style] For example: Expect(len(x)).Should(Equal(1)) This should be replaced with: Expect(x)).Should(HavelLen(1)) -* wrong nil assertions. We want to assert the item rather than a comparison result. +* wrong nil assertions. We want to assert the item rather than a comparison result. [Style] For example: Expect(x == nil).Should(BeTrue()) This should be replaced with: Expect(x).Should(BeNil()) -* wrong error assertions. For example: +* wrong error assertions. For example: [Style] Expect(err == nil).Should(BeTrue()) This should be replaced with: Expect(err).ShouldNot(HaveOccurred()) -* wrong boolean comparison, for example: +* wrong boolean comparison, for example: [Style] Expect(x == 8).Should(BeTrue()) This should be replaced with: Expect(x).Should(BeEqual(8)) -* replaces Equal(true/false) with BeTrue()/BeFalse() +* replaces Equal(true/false) with BeTrue()/BeFalse() [Style] -* replaces HaveLen(0) with BeEmpty() +* replaces HaveLen(0) with BeEmpty() [Style] ` // main assertion function func (l *ginkgoLinter) run(pass *analysis.Pass) (interface{}, error) { - if l.config.AllTrue() { - return nil, nil - } - for _, file := range pass.Files { fileConfig := l.config.Clone() cm := ast.NewCommentMap(pass.Fset, file, file.Comments) fileConfig.UpdateFromFile(cm) - if fileConfig.AllTrue() { - continue - } handler := gomegahandler.GetGomegaHandler(file) if handler == nil { // no gomega import => no use in gomega in this file; nothing to do here @@ -133,7 +158,6 @@ func (l *ginkgoLinter) run(pass *analysis.Pass) (interface{}, error) { } ast.Inspect(file, func(n ast.Node) bool { - stmt, ok := n.(*ast.ExprStmt) if !ok { return true @@ -152,61 +176,210 @@ func (l *ginkgoLinter) run(pass *analysis.Pass) (interface{}, error) { } assertionFunc, ok := assertionExp.Fun.(*ast.SelectorExpr) - if !ok || !isAssertionFunc(assertionFunc.Sel.Name) { + if !ok { + checkNoAssertion(pass, assertionExp, handler) return true } - actualArg := getActualArg(assertionFunc, handler) - if actualArg == nil { + if !isAssertionFunc(assertionFunc.Sel.Name) { + checkNoAssertion(pass, assertionExp, handler) return true } - return checkExpression(pass, config, actualArg, assertionExp, handler) + actualExpr := handler.GetActualExpr(assertionFunc) + if actualExpr == nil { + return true + } + return checkExpression(pass, config, assertionExp, actualExpr, handler) }) } return nil, nil } -func checkExpression(pass *analysis.Pass, config types.Config, actualArg ast.Expr, assertionExp *ast.CallExpr, handler gomegahandler.Handler) bool { - assertionExp = astcopy.CallExpr(assertionExp) - oldExpr := goFmt(pass.Fset, assertionExp) - if !bool(config.SuppressLen) && isActualIsLenFunc(actualArg) { +func checkExpression(pass *analysis.Pass, config types.Config, assertionExp *ast.CallExpr, actualExpr *ast.CallExpr, handler gomegahandler.Handler) bool { + expr := astcopy.CallExpr(assertionExp) + oldExpr := goFmt(pass.Fset, expr) - return checkLengthMatcher(assertionExp, pass, handler, oldExpr) - } else { - if nilable, compOp := getNilableFromComparison(actualArg); nilable != nil { - if isExprError(pass, nilable) { - if config.SuppressErr { - return true - } - } else if config.SuppressNil { + if checkAsyncAssertion(pass, config, expr, actualExpr, handler, oldExpr) { + return true + } + + actualArg := getActualArg(actualExpr, handler) + if actualArg == nil { + return true + } + + if !bool(config.SuppressLen) && isActualIsLenFunc(actualArg) { + return checkLengthMatcher(expr, pass, handler, oldExpr) + } else if nilable, compOp := getNilableFromComparison(actualArg); nilable != nil { + if isExprError(pass, nilable) { + if config.SuppressErr { return true } + } else if config.SuppressNil { + return true + } - return checkNilMatcher(assertionExp, pass, nilable, handler, compOp == token.NEQ, oldExpr) + return checkNilMatcher(expr, pass, nilable, handler, compOp == token.NEQ, oldExpr) - } else if first, second, op, ok := isComparison(pass, actualArg); ok { - return bool(config.SuppressCompare) || checkComparison(assertionExp, pass, handler, first, second, op, oldExpr) + } else if first, second, op, ok := isComparison(pass, actualArg); ok { + matcher, shouldContinue := startCheckComparison(expr, handler) + if !shouldContinue { + return false + } + if !bool(config.SuppressLen) && isActualIsLenFunc(first) { + if handleLenComparison(pass, expr, matcher, first, second, op, handler, oldExpr) { + return false + } + } + return bool(config.SuppressCompare) || checkComparison(expr, pass, matcher, handler, first, second, op, oldExpr) - } else if isExprError(pass, actualArg) { - return bool(config.SuppressErr) || checkNilError(pass, assertionExp, handler, actualArg, oldExpr) + } else if isExprError(pass, actualArg) { + return bool(config.SuppressErr) || checkNilError(pass, expr, handler, actualArg, oldExpr) - } else { - return handleAssertionOnly(pass, config, assertionExp, handler, actualArg, oldExpr) + } else if checkPointerComparison(pass, config, assertionExp, expr, actualArg, handler, oldExpr) { + return false + } else { + return handleAssertionOnly(pass, config, expr, handler, actualArg, oldExpr, true) + } +} + +// be careful - never change origExp!!! only modify its clone, expr!!! +func checkPointerComparison(pass *analysis.Pass, config types.Config, origExp *ast.CallExpr, expr *ast.CallExpr, actualArg ast.Expr, handler gomegahandler.Handler, oldExpr string) bool { + if !isPointer(pass, actualArg) { + return false + } + matcher, ok := origExp.Args[0].(*ast.CallExpr) + if !ok { + return false + } + + matcherFuncName, ok := handler.GetActualFuncName(matcher) + if !ok { + return false + } + + // not using recurse here, since we need the original expression, in order to get the TypeInfo, while we should not + // modify it. + for matcherFuncName == not { + reverseAssertionFuncLogic(expr) + expr.Args[0] = expr.Args[0].(*ast.CallExpr).Args[0] + matcher, ok = matcher.Args[0].(*ast.CallExpr) + if !ok { + return false + } + + matcherFuncName, ok = handler.GetActualFuncName(matcher) + if !ok { + return false } } + + switch matcherFuncName { + case equal, beIdenticalTo, beEquivalentTo: + arg := matcher.Args[0] + if isPointer(pass, arg) { + return false + } + if isNil(arg) { + return false + } + if isInterface(pass, arg) { + return false + } + case beFalse, beTrue, beNumerically: + default: + return false + } + + handleAssertionOnly(pass, config, expr, handler, actualArg, oldExpr, false) + + args := []ast.Expr{astcopy.CallExpr(expr.Args[0].(*ast.CallExpr))} + handler.ReplaceFunction(expr.Args[0].(*ast.CallExpr), ast.NewIdent(haveValue)) + expr.Args[0].(*ast.CallExpr).Args = args + report(pass, expr, comparePointerToValue, oldExpr) + + return true + } -func checkComparison(exp *ast.CallExpr, pass *analysis.Pass, handler gomegahandler.Handler, first ast.Expr, second ast.Expr, op token.Token, oldExp string) bool { +// check async assertion does not assert function call. This is a real bug in the test. In this case, the assertion is +// done on the returned value, instead of polling the result of a function, for instance. +func checkAsyncAssertion(pass *analysis.Pass, config types.Config, expr *ast.CallExpr, actualExpr *ast.CallExpr, handler gomegahandler.Handler, oldExpr string) bool { + funcName, ok := handler.GetActualFuncName(actualExpr) + if !ok { + return false + } + + var funcIndex int + switch funcName { + case eventually, consistently: + funcIndex = 0 + case eventuallyWithOffset, consistentlyWithOffset: + funcIndex = 1 + default: + return false + } + + if !config.SuppressAsync && len(actualExpr.Args) > funcIndex { + t := pass.TypesInfo.TypeOf(actualExpr.Args[funcIndex]) + + // skip context variable, if used as first argument + if "context.Context" == t.String() { + funcIndex++ + } + + if len(actualExpr.Args) > funcIndex { + if fun, funcCall := actualExpr.Args[funcIndex].(*ast.CallExpr); funcCall { + t = pass.TypesInfo.TypeOf(fun) + switch t.(type) { + // allow functions that return function or channel. + case *gotypes.Signature, *gotypes.Chan: + default: + actualExpr = handler.GetActualExpr(expr.Fun.(*ast.SelectorExpr)) + + if len(fun.Args) > 0 { + origArgs := actualExpr.Args + origFunc := actualExpr.Fun + actualExpr.Args = fun.Args + + origArgs[funcIndex] = fun.Fun + call := &ast.SelectorExpr{ + Sel: ast.NewIdent("WithArguments"), + X: &ast.CallExpr{ + Fun: origFunc, + Args: origArgs, + }, + } + + actualExpr.Fun = call + actualExpr.Args = fun.Args + } else { + actualExpr.Args[funcIndex] = fun.Fun + } + + handleAssertionOnly(pass, config, expr, handler, actualExpr, oldExpr, false) + report(pass, expr, fmt.Sprintf(valueInEventually, funcName, funcName)+"; consider using `%s` instead", oldExpr) + return true + } + } + } + } + + handleAssertionOnly(pass, config, expr, handler, actualExpr, oldExpr, true) + return true +} + +func startCheckComparison(exp *ast.CallExpr, handler gomegahandler.Handler) (*ast.CallExpr, bool) { matcher, ok := exp.Args[0].(*ast.CallExpr) if !ok { - return true + return nil, false } matcherFuncName, ok := handler.GetActualFuncName(matcher) if !ok { - return true + return nil, false } switch matcherFuncName { @@ -216,35 +389,38 @@ func checkComparison(exp *ast.CallExpr, pass *analysis.Pass, handler gomegahandl case equal: boolean, found := matcher.Args[0].(*ast.Ident) if !found { - return true + return nil, false } if boolean.Name == "false" { reverseAssertionFuncLogic(exp) } else if boolean.Name != "true" { - return true + return nil, false } case not: reverseAssertionFuncLogic(exp) exp.Args[0] = exp.Args[0].(*ast.CallExpr).Args[0] - return checkComparison(exp, pass, handler, first, second, op, oldExp) + return startCheckComparison(exp, handler) default: - return true + return nil, false } + return matcher, true +} + +func checkComparison(exp *ast.CallExpr, pass *analysis.Pass, matcher *ast.CallExpr, handler gomegahandler.Handler, first ast.Expr, second ast.Expr, op token.Token, oldExp string) bool { fun, ok := exp.Fun.(*ast.SelectorExpr) if !ok { return true } - call, ok := fun.X.(*ast.CallExpr) - if !ok { + call := handler.GetActualExpr(fun) + if call == nil { return true } - call.Args = []ast.Expr{first} switch op { case token.EQL: handleEqualComparison(pass, matcher, first, second, handler) @@ -253,6 +429,9 @@ func checkComparison(exp *ast.CallExpr, pass *analysis.Pass, handler gomegahandl reverseAssertionFuncLogic(exp) handleEqualComparison(pass, matcher, first, second, handler) case token.GTR, token.GEQ, token.LSS, token.LEQ: + if !isNumeric(pass, first) { + return true + } handler.ReplaceFunction(matcher, ast.NewIdent(beNumerically)) matcher.Args = []ast.Expr{ &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf(`"%s"`, op.String())}, @@ -262,6 +441,7 @@ func checkComparison(exp *ast.CallExpr, pass *analysis.Pass, handler gomegahandl return true } + call.Args = []ast.Expr{first} report(pass, exp, wrongCompareWarningTemplate, oldExp) return false } @@ -284,6 +464,37 @@ func handleEqualComparison(pass *analysis.Pass, matcher *ast.CallExpr, first ast } } +func handleLenComparison(pass *analysis.Pass, exp *ast.CallExpr, matcher *ast.CallExpr, first ast.Expr, second ast.Expr, op token.Token, handler gomegahandler.Handler, oldExpr string) bool { + switch op { + case token.EQL: + case token.NEQ: + reverseAssertionFuncLogic(exp) + default: + return false + } + + var eql *ast.Ident + if isZero(pass, second) { + eql = ast.NewIdent(beEmpty) + } else { + eql = ast.NewIdent(haveLen) + matcher.Args = []ast.Expr{second} + } + + handler.ReplaceFunction(matcher, eql) + firstLen, ok := first.(*ast.CallExpr) // assuming it's len() + if !ok { + return false // should never happen + } + + val := firstLen.Args[0] + fun := handler.GetActualExpr(exp.Fun.(*ast.SelectorExpr)) + fun.Args = []ast.Expr{val} + + report(pass, exp, wrongLengthWarningTemplate, oldExpr) + return true +} + // Check if the "actual" argument is a call to the golang built-in len() function func isActualIsLenFunc(actualArg ast.Expr) bool { lenArgExp, ok := actualArg.(*ast.CallExpr) @@ -421,12 +632,12 @@ func checkNilError(pass *analysis.Pass, assertionExp *ast.CallExpr, handler gome // Equal(true) => BeTrue() // Equal(false) => BeFalse() // HaveLen(0) => BeEmpty() -func handleAssertionOnly(pass *analysis.Pass, config types.Config, assertionExp *ast.CallExpr, handler gomegahandler.Handler, actualArg ast.Expr, oldExpr string) bool { - if len(assertionExp.Args) == 0 { +func handleAssertionOnly(pass *analysis.Pass, config types.Config, expr *ast.CallExpr, handler gomegahandler.Handler, actualArg ast.Expr, oldExpr string, shouldReport bool) bool { + if len(expr.Args) == 0 { return true } - equalFuncExpr, ok := assertionExp.Args[0].(*ast.CallExpr) + equalFuncExpr, ok := expr.Args[0].(*ast.CallExpr) if !ok { return true } @@ -460,7 +671,12 @@ func handleAssertionOnly(pass *analysis.Pass, config types.Config, assertionExp replacement = beTrue template = wrongBoolWarningTemplate case "false": - replacement = beFalse + if isNegativeAssertion(expr) { + reverseAssertionFuncLogic(expr) + replacement = beTrue + } else { + replacement = beFalse + } template = wrongBoolWarningTemplate default: return true @@ -469,8 +685,20 @@ func handleAssertionOnly(pass *analysis.Pass, config types.Config, assertionExp handler.ReplaceFunction(equalFuncExpr, ast.NewIdent(replacement)) equalFuncExpr.Args = nil - report(pass, assertionExp, template, oldExpr) + if shouldReport { + report(pass, expr, template, oldExpr) + } + + return false + case beFalse: + if isNegativeAssertion(expr) { + reverseAssertionFuncLogic(expr) + handler.ReplaceFunction(equalFuncExpr, ast.NewIdent(beTrue)) + if shouldReport { + report(pass, expr, doubleNegativeWarningTemplate, oldExpr) + } + } return false case haveLen: @@ -482,7 +710,9 @@ func handleAssertionOnly(pass *analysis.Pass, config types.Config, assertionExp if isZero(pass, equalFuncExpr.Args[0]) { handler.ReplaceFunction(equalFuncExpr, ast.NewIdent(beEmpty)) equalFuncExpr.Args = nil - report(pass, assertionExp, wrongLengthWarningTemplate, oldExpr) + if shouldReport { + report(pass, expr, wrongLengthWarningTemplate, oldExpr) + } return false } } @@ -490,9 +720,9 @@ func handleAssertionOnly(pass *analysis.Pass, config types.Config, assertionExp return true case not: - reverseAssertionFuncLogic(assertionExp) - assertionExp.Args[0] = assertionExp.Args[0].(*ast.CallExpr).Args[0] - return handleAssertionOnly(pass, config, assertionExp, handler, actualArg, oldExpr) + reverseAssertionFuncLogic(expr) + expr.Args[0] = expr.Args[0].(*ast.CallExpr).Args[0] + return handleAssertionOnly(pass, config, expr, handler, actualArg, oldExpr, shouldReport) default: return true } @@ -522,14 +752,9 @@ func isZero(pass *analysis.Pass, arg ast.Expr) bool { return false } -// checks that the function is an assertion's actual function and return the "actual" parameter. If the function -// is not assertion's actual function, return nil. -func getActualArg(assertionFunc *ast.SelectorExpr, handler gomegahandler.Handler) ast.Expr { - actualExpr, ok := assertionFunc.X.(*ast.CallExpr) - if !ok { - return nil - } - +// getActualArg checks that the function is an assertion's actual function and return the "actual" parameter. If the +// function is not assertion's actual function, return nil. +func getActualArg(actualExpr *ast.CallExpr, handler gomegahandler.Handler) ast.Expr { funcName, ok := handler.GetActualFuncName(actualExpr) if !ok { return nil @@ -636,6 +861,11 @@ func reverseAssertionFuncLogic(exp *ast.CallExpr) { assertionFunc.Name = reverseassertion.ChangeAssertionLogic(assertionFunc.Name) } +func isNegativeAssertion(exp *ast.CallExpr) bool { + assertionFunc := exp.Fun.(*ast.SelectorExpr).Sel + return reverseassertion.IsNegativeLogic(assertionFunc.Name) +} + func handleEqualMatcher(matcher *ast.CallExpr, pass *analysis.Pass, exp *ast.CallExpr, handler gomegahandler.Handler, oldExp string) { equalTo, ok := matcher.Args[0].(*ast.BasicLit) if ok { @@ -695,6 +925,7 @@ func handleNilComparisonErr(pass *analysis.Pass, exp *ast.CallExpr, nilable ast. return newFuncName, isItError } + func isAssertionFunc(name string) bool { switch name { case "To", "ToNot", "NotTo", "Should", "ShouldNot": @@ -704,13 +935,15 @@ func isAssertionFunc(name string) bool { } func reportLengthAssertion(pass *analysis.Pass, expr *ast.CallExpr, handler gomegahandler.Handler, oldExpr string) { - replaceLenActualArg(expr.Fun.(*ast.SelectorExpr).X.(*ast.CallExpr), handler) + actualExpr := handler.GetActualExpr(expr.Fun.(*ast.SelectorExpr)) + replaceLenActualArg(actualExpr, handler) report(pass, expr, wrongLengthWarningTemplate, oldExpr) } func reportNilAssertion(pass *analysis.Pass, expr *ast.CallExpr, handler gomegahandler.Handler, nilable ast.Expr, notEqual bool, oldExpr string, isItError bool) { - changed := replaceNilActualArg(expr.Fun.(*ast.SelectorExpr).X.(*ast.CallExpr), handler, nilable) + actualExpr := handler.GetActualExpr(expr.Fun.(*ast.SelectorExpr)) + changed := replaceNilActualArg(actualExpr, handler, nilable) if !changed { return } @@ -746,6 +979,17 @@ func report(pass *analysis.Pass, expr *ast.CallExpr, messageTemplate, oldExpr st }) } +func reportNoFix(pass *analysis.Pass, pos token.Pos, message string, args ...any) { + if len(args) > 0 { + message = fmt.Sprintf(message, args...) + } + + pass.Report(analysis.Diagnostic{ + Pos: pos, + Message: message, + }) +} + func getNilableFromComparison(actualArg ast.Expr) (ast.Expr, token.Token) { bin, ok := actualArg.(*ast.BinaryExpr) if !ok { @@ -841,3 +1085,48 @@ func isExprError(pass *analysis.Pass, expr ast.Expr) bool { } return false } + +func isPointer(pass *analysis.Pass, expr ast.Expr) bool { + t := pass.TypesInfo.TypeOf(expr) + _, ok := t.(*gotypes.Pointer) + return ok +} + +func isInterface(pass *analysis.Pass, expr ast.Expr) bool { + t := pass.TypesInfo.TypeOf(expr) + _, ok := t.(*gotypes.Named) + return ok +} + +func isNumeric(pass *analysis.Pass, node ast.Expr) bool { + t := pass.TypesInfo.TypeOf(node) + + switch t.String() { + case "int", "uint", "int8", "uint8", "int16", "uint16", "int32", "uint32", "int64", "uint64", "float32", "float64": + return true + } + return false +} + +func checkNoAssertion(pass *analysis.Pass, expr *ast.CallExpr, handler gomegahandler.Handler) { + funcName, ok := handler.GetActualFuncName(expr) + if ok { + if isActualFunc(funcName) { + reportNoFix(pass, expr.Pos(), missingAssertionMessage, funcName) + } else if isActualAsyncFunc(funcName) { + reportNoFix(pass, expr.Pos(), missingAsyncAssertionMessage, funcName) + } + } +} + +func isActualFunc(name string) bool { + return name == expect || name == expectWithOffset +} + +func isActualAsyncFunc(name string) bool { + switch name { + case eventually, eventuallyWithOffset, consistently, consistentlyWithOffset: + return true + } + return false +} diff --git a/vendor/github.com/nunnatsa/ginkgolinter/gomegahandler/handler.go b/vendor/github.com/nunnatsa/ginkgolinter/gomegahandler/handler.go index d57ba291..1e876514 100644 --- a/vendor/github.com/nunnatsa/ginkgolinter/gomegahandler/handler.go +++ b/vendor/github.com/nunnatsa/ginkgolinter/gomegahandler/handler.go @@ -16,6 +16,8 @@ type Handler interface { getDefFuncName(expr *ast.CallExpr) string getFieldType(field *ast.Field) string + + GetActualExpr(assertionFunc *ast.SelectorExpr) *ast.CallExpr } // GetGomegaHandler returns a gomegar handler according to the way gomega was imported in the specific file @@ -50,7 +52,13 @@ func (h dotHandler) GetActualFuncName(expr *ast.CallExpr) (string, bool) { case *ast.SelectorExpr: if isGomegaVar(actualFunc.X, h) { return actualFunc.Sel.Name, true + } else { + if x, ok := actualFunc.X.(*ast.CallExpr); ok { + return h.GetActualFuncName(x) + } } + case *ast.CallExpr: + return h.GetActualFuncName(actualFunc) } return "", false } @@ -93,18 +101,21 @@ func (g nameHandler) GetActualFuncName(expr *ast.CallExpr) (string, bool) { return "", false } - x, ok := selector.X.(*ast.Ident) - if !ok { - return "", false - } - - if x.Name != string(g) { - if !isGomegaVar(x, g) { - return "", false + switch x := selector.X.(type) { + case *ast.Ident: + if x.Name != string(g) { + if !isGomegaVar(x, g) { + return "", false + } } + + return selector.Sel.Name, true + + case *ast.CallExpr: + return g.GetActualFuncName(x) } - return selector.Sel.Name, true + return "", false } // ReplaceFunction replaces the function with another one, for fix suggestions @@ -165,3 +176,56 @@ func isGomegaVar(x ast.Expr, handler Handler) bool { } return false } + +func (h dotHandler) GetActualExpr(assertionFunc *ast.SelectorExpr) *ast.CallExpr { + actualExpr, ok := assertionFunc.X.(*ast.CallExpr) + if !ok { + return nil + } + + switch fun := actualExpr.Fun.(type) { + case *ast.Ident: + return actualExpr + case *ast.SelectorExpr: + if isHelperMethods(fun.Sel.Name) { + return h.GetActualExpr(fun) + } + if isGomegaVar(fun.X, h) { + return actualExpr + } + } + return nil +} + +func (g nameHandler) GetActualExpr(assertionFunc *ast.SelectorExpr) *ast.CallExpr { + actualExpr, ok := assertionFunc.X.(*ast.CallExpr) + if !ok { + return nil + } + + switch fun := actualExpr.Fun.(type) { + case *ast.Ident: + return actualExpr + case *ast.SelectorExpr: + if x, ok := fun.X.(*ast.Ident); ok && x.Name == string(g) { + return actualExpr + } + if isHelperMethods(fun.Sel.Name) { + return g.GetActualExpr(fun) + } + + if isGomegaVar(fun.X, g) { + return actualExpr + } + } + return nil +} + +func isHelperMethods(funcName string) bool { + switch funcName { + case "WithOffset", "WithTimeout", "WithPolling", "Within", "ProbeEvery", "WithContext", "WithArguments", "MustPassRepeatedly": + return true + } + + return false +} diff --git a/vendor/github.com/nunnatsa/ginkgolinter/reverseassertion/reverse_assertion.go b/vendor/github.com/nunnatsa/ginkgolinter/reverseassertion/reverse_assertion.go index 9b4a6f3a..1dbd8981 100644 --- a/vendor/github.com/nunnatsa/ginkgolinter/reverseassertion/reverse_assertion.go +++ b/vendor/github.com/nunnatsa/ginkgolinter/reverseassertion/reverse_assertion.go @@ -18,6 +18,14 @@ func ChangeAssertionLogic(funcName string) string { return funcName } +func IsNegativeLogic(funcName string) bool { + switch funcName { + case "ToNot", "NotTo", "ShouldNot": + return true + } + return false +} + var reverseCompareOperators = map[token.Token]token.Token{ token.LSS: token.GTR, token.GTR: token.LSS, diff --git a/vendor/github.com/nunnatsa/ginkgolinter/types/config.go b/vendor/github.com/nunnatsa/ginkgolinter/types/config.go index 0cccab27..43145d84 100644 --- a/vendor/github.com/nunnatsa/ginkgolinter/types/config.go +++ b/vendor/github.com/nunnatsa/ginkgolinter/types/config.go @@ -12,6 +12,7 @@ const ( suppressNilAssertionWarning = suppressPrefix + "ignore-nil-assert-warning" suppressErrAssertionWarning = suppressPrefix + "ignore-err-assert-warning" suppressCompareAssertionWarning = suppressPrefix + "ignore-compare-assert-warning" + suppressAsyncAsertWarning = suppressPrefix + "ignore-async-assert-warning" ) type Config struct { @@ -19,11 +20,12 @@ type Config struct { SuppressNil Boolean SuppressErr Boolean SuppressCompare Boolean + SuppressAsync Boolean AllowHaveLen0 Boolean } func (s *Config) AllTrue() bool { - return bool(s.SuppressLen && s.SuppressNil && s.SuppressErr && s.SuppressCompare) + return bool(s.SuppressLen && s.SuppressNil && s.SuppressErr && s.SuppressCompare && s.SuppressAsync) } func (s *Config) Clone() Config { @@ -32,6 +34,7 @@ func (s *Config) Clone() Config { SuppressNil: s.SuppressNil, SuppressErr: s.SuppressErr, SuppressCompare: s.SuppressCompare, + SuppressAsync: s.SuppressAsync, AllowHaveLen0: s.AllowHaveLen0, } } @@ -54,6 +57,7 @@ func (s *Config) UpdateFromComment(commentGroup []*ast.CommentGroup) { s.SuppressNil = s.SuppressNil || (comment == suppressNilAssertionWarning) s.SuppressErr = s.SuppressErr || (comment == suppressErrAssertionWarning) s.SuppressCompare = s.SuppressCompare || (comment == suppressCompareAssertionWarning) + s.SuppressAsync = s.SuppressAsync || (comment == suppressAsyncAsertWarning) } } } diff --git a/vendor/github.com/nunnatsa/ginkgolinter/version/version.go b/vendor/github.com/nunnatsa/ginkgolinter/version/version.go new file mode 100644 index 00000000..7bf181a8 --- /dev/null +++ b/vendor/github.com/nunnatsa/ginkgolinter/version/version.go @@ -0,0 +1,14 @@ +package version + +var ( + version = "unknown" + gitHash = "unknown" +) + +func Version() string { + return version +} + +func GitHash() string { + return gitHash +} diff --git a/vendor/github.com/polyfloyd/go-errorlint/errorlint/allowed.go b/vendor/github.com/polyfloyd/go-errorlint/errorlint/allowed.go index c9dcf5e5..366b5c6b 100644 --- a/vendor/github.com/polyfloyd/go-errorlint/errorlint/allowed.go +++ b/vendor/github.com/polyfloyd/go-errorlint/errorlint/allowed.go @@ -10,8 +10,8 @@ var allowedErrors = []struct { fun string }{ // pkg/archive/tar - {err: "io.EOF", fun: "(*tar.Reader).Next"}, - {err: "io.EOF", fun: "(*tar.Reader).Read"}, + {err: "io.EOF", fun: "(*archive/tar.Reader).Next"}, + {err: "io.EOF", fun: "(*archive/tar.Reader).Read"}, // pkg/bufio {err: "io.EOF", fun: "(*bufio.Reader).Discard"}, {err: "io.EOF", fun: "(*bufio.Reader).Peek"}, @@ -154,10 +154,10 @@ func assigningCallExprs(info *TypesInfoExt, subject *ast.Ident) []*ast.CallExpr assigningExpr := assignment.Rhs[0] // If the assignment is comprised of multiple expressions, find out - // which LHS expression we should use by finding its index in the LHS. - if len(assignment.Rhs) > 1 { + // which RHS expression we should use by finding its index in the LHS. + if len(assignment.Lhs) == len(assignment.Rhs) { for i, lhs := range assignment.Lhs { - if subject.Name == lhs.(*ast.Ident).Name { + if ident, ok := lhs.(*ast.Ident); ok && subject.Name == ident.Name { assigningExpr = assignment.Rhs[i] break } diff --git a/vendor/github.com/securego/gosec/v2/.golangci.yml b/vendor/github.com/securego/gosec/v2/.golangci.yml index 64e4e451..b12140a2 100644 --- a/vendor/github.com/securego/gosec/v2/.golangci.yml +++ b/vendor/github.com/securego/gosec/v2/.golangci.yml @@ -1,33 +1,41 @@ linters: enable: - - asciicheck - - bodyclose - - deadcode - - depguard - - dogsled - - durationcheck - - errcheck - - errorlint - - exportloopref - - gci - - gofmt - - gofumpt - - goimports - - gosec - - gosimple - - govet - - importas - - ineffassign - - megacheck - - misspell - - nakedret - - nolintlint - - revive - - staticcheck - - structcheck - - typecheck - - unconvert - - unparam - - unused - - varcheck - - wastedassign + - asciicheck + - bodyclose + - depguard + - dogsled + - durationcheck + - errcheck + - errorlint + - exportloopref + - gci + - ginkgolinter + - gofmt + - gofumpt + - goimports + - gosec + - gosimple + - govet + - importas + - ineffassign + - megacheck + - misspell + - nakedret + - nolintlint + - revive + - staticcheck + - typecheck + - unconvert + - unparam + - unused + - wastedassign + +linters-settings: + gci: + sections: + - standard + - default + - prefix(github.com/securego) + +run: + timeout: 5m diff --git a/vendor/github.com/securego/gosec/v2/.goreleaser.yml b/vendor/github.com/securego/gosec/v2/.goreleaser.yml index 25a81b52..e3c903e7 100644 --- a/vendor/github.com/securego/gosec/v2/.goreleaser.yml +++ b/vendor/github.com/securego/gosec/v2/.goreleaser.yml @@ -26,6 +26,11 @@ builds: signs: - cmd: cosign stdin: '{{ .Env.COSIGN_PASSWORD}}' - args: ["sign-blob", "--key=/tmp/cosign.key", "--output=${signature}", "${artifact}"] + args: + - "sign-blob" + - "--key=/tmp/cosign.key" + - "--output=${signature}" + - "${artifact}" + - "--yes" artifacts: all diff --git a/vendor/github.com/securego/gosec/v2/Dockerfile b/vendor/github.com/securego/gosec/v2/Dockerfile index b57c981f..1bf94da7 100644 --- a/vendor/github.com/securego/gosec/v2/Dockerfile +++ b/vendor/github.com/securego/gosec/v2/Dockerfile @@ -1,11 +1,11 @@ ARG GO_VERSION FROM golang:${GO_VERSION}-alpine AS builder -RUN apk add --no-cache ca-certificates make git curl gcc libc-dev -RUN mkdir -p /build +RUN apk add --no-cache ca-certificates make git curl gcc libc-dev \ + && mkdir -p /build WORKDIR /build COPY . /build/ -RUN go mod download -RUN make build-linux +RUN go mod download \ + && make build-linux FROM golang:${GO_VERSION}-alpine RUN apk add --no-cache ca-certificates bash git gcc libc-dev openssh diff --git a/vendor/github.com/securego/gosec/v2/Makefile b/vendor/github.com/securego/gosec/v2/Makefile index 093c8a99..09303d11 100644 --- a/vendor/github.com/securego/gosec/v2/Makefile +++ b/vendor/github.com/securego/gosec/v2/Makefile @@ -87,4 +87,7 @@ image-push: image docker push $(IMAGE_REPO)/$(BIN):$(GIT_TAG) docker push $(IMAGE_REPO)/$(BIN):latest -.PHONY: test build clean release image image-push +tlsconfig: + go generate ./... + +.PHONY: test build clean release image image-push tlsconfig diff --git a/vendor/github.com/securego/gosec/v2/README.md b/vendor/github.com/securego/gosec/v2/README.md index bf7aa088..71e032d8 100644 --- a/vendor/github.com/securego/gosec/v2/README.md +++ b/vendor/github.com/securego/gosec/v2/README.md @@ -68,7 +68,7 @@ jobs: GO111MODULE: on steps: - name: Checkout Source - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Run Gosec Security Scanner uses: securego/gosec@master with: @@ -98,7 +98,7 @@ jobs: GO111MODULE: on steps: - name: Checkout Source - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Run Gosec Security Scanner uses: securego/gosec@master with: @@ -157,7 +157,6 @@ directory you can supply `./...` as the input argument. - G304: File path provided as taint input - G305: File traversal when extracting zip/tar archive - G306: Poor file permissions used when writing to a new file -- G307: Deferring a method which returns an error - G401: Detect the usage of DES, RC4, MD5 or SHA1 - G402: Look for bad TLS connection settings - G403: Ensure minimum RSA key length of 2048 bits @@ -172,6 +171,7 @@ directory you can supply `./...` as the input argument. ### Retired rules - G105: Audit the use of math/big.Int.Exp - [CVE is fixed](https://github.com/golang/go/issues/15184) +- G307: Deferring a method which returns an error - causing more inconvenience than fixing a security issue, despite the details from this [blog post](https://www.joeshaw.org/dont-defer-close-on-writable-files/) ### Selecting rules @@ -188,7 +188,7 @@ $ gosec -exclude=G303 ./... ### CWE Mapping -Every issue detected by `gosec` is mapped to a [CWE (Common Weakness Enumeration)](http://cwe.mitre.org/data/index.html) which describes in more generic terms the vulnerability. The exact mapping can be found [here](https://github.com/securego/gosec/blob/master/issue.go#L50). +Every issue detected by `gosec` is mapped to a [CWE (Common Weakness Enumeration)](http://cwe.mitre.org/data/index.html) which describes in more generic terms the vulnerability. The exact mapping can be found [here](https://github.com/securego/gosec/blob/master/issue/issue.go#L50). ### Configuration diff --git a/vendor/github.com/securego/gosec/v2/action.yml b/vendor/github.com/securego/gosec/v2/action.yml index aab6c803..0320f0c2 100644 --- a/vendor/github.com/securego/gosec/v2/action.yml +++ b/vendor/github.com/securego/gosec/v2/action.yml @@ -10,7 +10,7 @@ inputs: runs: using: 'docker' - image: 'docker://securego/gosec' + image: 'docker://securego/gosec:2.15.0' args: - ${{ inputs.args }} diff --git a/vendor/github.com/securego/gosec/v2/analyzer.go b/vendor/github.com/securego/gosec/v2/analyzer.go index 5f778791..830d338e 100644 --- a/vendor/github.com/securego/gosec/v2/analyzer.go +++ b/vendor/github.com/securego/gosec/v2/analyzer.go @@ -31,6 +31,10 @@ import ( "strings" "sync" + "github.com/securego/gosec/v2/analyzers" + "github.com/securego/gosec/v2/issue" + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/buildssa" "golang.org/x/tools/go/packages" ) @@ -42,7 +46,10 @@ const LoadMode = packages.NeedName | packages.NeedTypes | packages.NeedTypesSizes | packages.NeedTypesInfo | - packages.NeedSyntax + packages.NeedSyntax | + packages.NeedModule | + packages.NeedEmbedFiles | + packages.NeedEmbedPatterns const externalSuppressionJustification = "Globally suppressed." @@ -60,12 +67,24 @@ type Context struct { Pkg *types.Package PkgFiles []*ast.File Root *ast.File - Config Config Imports *ImportTracker - Ignores []map[string][]SuppressionInfo + Config Config + Ignores []map[string][]issue.SuppressionInfo PassedValues map[string]interface{} } +// GetFileAtNodePos returns the file at the node position in the file set available in the context. +func (ctx *Context) GetFileAtNodePos(node ast.Node) *token.File { + return ctx.FileSet.File(node.Pos()) +} + +// NewIssue creates a new issue +func (ctx *Context) NewIssue(node ast.Node, ruleID, desc string, + severity, confidence issue.Score, +) *issue.Issue { + return issue.New(ctx.GetFileAtNodePos(node), node, ruleID, desc, severity, confidence) +} + // Metrics used when reporting information about a scanning run. type Metrics struct { NumFiles int `json:"files"` @@ -82,7 +101,7 @@ type Analyzer struct { context *Context config Config logger *log.Logger - issues []*Issue + issues []*issue.Issue stats *Metrics errors map[string][]Error // keys are file paths; values are the golang errors in those files tests bool @@ -90,13 +109,7 @@ type Analyzer struct { showIgnored bool trackSuppressions bool concurrency int -} - -// SuppressionInfo object is to record the kind and the justification that used -// to suppress violations. -type SuppressionInfo struct { - Kind string `json:"kind"` - Justification string `json:"justification"` + analyzerList []*analysis.Analyzer } // NewAnalyzer builds a new analyzer. @@ -119,13 +132,14 @@ func NewAnalyzer(conf Config, tests bool, excludeGenerated bool, trackSuppressio context: &Context{}, config: conf, logger: logger, - issues: make([]*Issue, 0, 16), + issues: make([]*issue.Issue, 0, 16), stats: &Metrics{}, errors: make(map[string][]Error), tests: tests, concurrency: concurrency, excludeGenerated: excludeGenerated, trackSuppressions: trackSuppressions, + analyzerList: analyzers.BuildDefaultAnalyzers(), } } @@ -216,7 +230,10 @@ func (gosec *Analyzer) Process(buildTags []string, packagePaths ...string) error wg.Wait() // wait for the goroutines to stop return fmt.Errorf("parsing errors in pkg %q: %w", pkg.Name, err) } - gosec.Check(pkg) + gosec.CheckRules(pkg) + if on, err := gosec.config.IsGlobalEnabled(SSA); err == nil && on { + gosec.CheckAnalyzers(pkg) + } } } } @@ -267,8 +284,8 @@ func (gosec *Analyzer) load(pkgPath string, conf *packages.Config) ([]*packages. return pkgs, nil } -// Check runs analysis on the given package -func (gosec *Analyzer) Check(pkg *packages.Package) { +// CheckRules runs analysis on the given package +func (gosec *Analyzer) CheckRules(pkg *packages.Package) { gosec.logger.Println("Checking package:", pkg.Name) for _, file := range pkg.Syntax { fp := pkg.Fset.File(file.Pos()) @@ -303,6 +320,70 @@ func (gosec *Analyzer) Check(pkg *packages.Package) { } } +// CheckAnalyzers runs analyzers on a given package +func (gosec *Analyzer) CheckAnalyzers(pkg *packages.Package) { + ssaPass := &analysis.Pass{ + Analyzer: buildssa.Analyzer, + Fset: pkg.Fset, + Files: pkg.Syntax, + OtherFiles: pkg.OtherFiles, + IgnoredFiles: pkg.IgnoredFiles, + Pkg: pkg.Types, + TypesInfo: pkg.TypesInfo, + TypesSizes: pkg.TypesSizes, + ResultOf: nil, + Report: nil, + ImportObjectFact: nil, + ExportObjectFact: nil, + ImportPackageFact: nil, + ExportPackageFact: nil, + AllObjectFacts: nil, + AllPackageFacts: nil, + } + ssaResult, err := ssaPass.Analyzer.Run(ssaPass) + if err != nil { + gosec.logger.Printf("Error running SSA analyser on package %q: %s", pkg.Name, err) + return + } + resultMap := map[*analysis.Analyzer]interface{}{ + buildssa.Analyzer: &analyzers.SSAAnalyzerResult{ + Config: gosec.Config(), + Logger: gosec.logger, + SSA: ssaResult.(*buildssa.SSA), + }, + } + for _, analyzer := range gosec.analyzerList { + pass := &analysis.Pass{ + Analyzer: analyzer, + Fset: pkg.Fset, + Files: pkg.Syntax, + OtherFiles: pkg.OtherFiles, + IgnoredFiles: pkg.IgnoredFiles, + Pkg: pkg.Types, + TypesInfo: pkg.TypesInfo, + TypesSizes: pkg.TypesSizes, + ResultOf: resultMap, + Report: func(d analysis.Diagnostic) {}, + ImportObjectFact: nil, + ExportObjectFact: nil, + ImportPackageFact: nil, + ExportPackageFact: nil, + AllObjectFacts: nil, + AllPackageFacts: nil, + } + result, err := pass.Analyzer.Run(pass) + if err != nil { + gosec.logger.Printf("Error running analyzer %s: %s\n", analyzer.Name, err) + continue + } + if result != nil { + if aissue, ok := result.(*issue.Issue); ok { + gosec.updateIssues(aissue, false, []issue.SuppressionInfo{}) + } + } + } +} + func isGeneratedFile(file *ast.File) bool { for _, comment := range file.Comments { for _, row := range comment.List { @@ -364,7 +445,7 @@ func (gosec *Analyzer) AppendError(file string, err error) { } // ignore a node (and sub-tree) if it is tagged with a nosec tag comment -func (gosec *Analyzer) ignore(n ast.Node) map[string]SuppressionInfo { +func (gosec *Analyzer) ignore(n ast.Node) map[string]issue.SuppressionInfo { if groups, ok := gosec.context.Comments[n]; ok && !gosec.ignoreNosec { // Checks if an alternative for #nosec is set and, if not, uses the default. @@ -401,13 +482,13 @@ func (gosec *Analyzer) ignore(n ast.Node) map[string]SuppressionInfo { re := regexp.MustCompile(`(G\d{3})`) matches := re.FindAllStringSubmatch(directive, -1) - suppression := SuppressionInfo{ + suppression := issue.SuppressionInfo{ Kind: "inSource", Justification: justification, } // Find the rule IDs to ignore. - ignores := make(map[string]SuppressionInfo) + ignores := make(map[string]issue.SuppressionInfo) for _, v := range matches { ignores[v[1]] = suppression } @@ -426,25 +507,42 @@ func (gosec *Analyzer) ignore(n ast.Node) map[string]SuppressionInfo { // Visit runs the gosec visitor logic over an AST created by parsing go code. // Rule methods added with AddRule will be invoked as necessary. func (gosec *Analyzer) Visit(n ast.Node) ast.Visitor { - // If we've reached the end of this branch, pop off the ignores stack. - if n == nil { - if len(gosec.context.Ignores) > 0 { - gosec.context.Ignores = gosec.context.Ignores[1:] - } + ignores, ok := gosec.updateIgnoredRules(n) + if !ok { return gosec } + + // Using ast.File instead of ast.ImportSpec, so that we can track all imports at once. switch i := n.(type) { case *ast.File: - // Using ast.File instead of ast.ImportSpec, so that we can track - // all imports at once. gosec.context.Imports.TrackFile(i) } + for _, rule := range gosec.ruleset.RegisteredFor(n) { + suppressions, ignored := gosec.updateSuppressions(rule.ID(), ignores) + issue, err := rule.Match(n, gosec.context) + if err != nil { + file, line := GetLocation(n, gosec.context) + file = path.Base(file) + gosec.logger.Printf("Rule error: %v => %s (%s:%d)\n", reflect.TypeOf(rule), err, file, line) + } + gosec.updateIssues(issue, ignored, suppressions) + } + return gosec +} + +func (gosec *Analyzer) updateIgnoredRules(n ast.Node) (map[string][]issue.SuppressionInfo, bool) { + if n == nil { + if len(gosec.context.Ignores) > 0 { + gosec.context.Ignores = gosec.context.Ignores[1:] + } + return nil, false + } // Get any new rule exclusions. ignoredRules := gosec.ignore(n) // Now create the union of exclusions. - ignores := map[string][]SuppressionInfo{} + ignores := map[string][]issue.SuppressionInfo{} if len(gosec.context.Ignores) > 0 { for k, v := range gosec.context.Ignores[0] { ignores[k] = v @@ -456,59 +554,57 @@ func (gosec *Analyzer) Visit(n ast.Node) ast.Visitor { } // Push the new set onto the stack. - gosec.context.Ignores = append([]map[string][]SuppressionInfo{ignores}, gosec.context.Ignores...) + gosec.context.Ignores = append([]map[string][]issue.SuppressionInfo{ignores}, gosec.context.Ignores...) - for _, rule := range gosec.ruleset.RegisteredFor(n) { - // Check if all rules are ignored. - generalSuppressions, generalIgnored := ignores[aliasOfAllRules] - // Check if the specific rule is ignored - ruleSuppressions, ruleIgnored := ignores[rule.ID()] - - ignored := generalIgnored || ruleIgnored - suppressions := append(generalSuppressions, ruleSuppressions...) - - // Track external suppressions. - if gosec.ruleset.IsRuleSuppressed(rule.ID()) { - ignored = true - suppressions = append(suppressions, SuppressionInfo{ - Kind: "external", - Justification: externalSuppressionJustification, - }) - } + return ignores, true +} - issue, err := rule.Match(n, gosec.context) - if err != nil { - file, line := GetLocation(n, gosec.context) - file = path.Base(file) - gosec.logger.Printf("Rule error: %v => %s (%s:%d)\n", reflect.TypeOf(rule), err, file, line) +func (gosec *Analyzer) updateSuppressions(id string, ignores map[string][]issue.SuppressionInfo) ([]issue.SuppressionInfo, bool) { + // Check if all rules are ignored. + generalSuppressions, generalIgnored := ignores[aliasOfAllRules] + // Check if the specific rule is ignored + ruleSuppressions, ruleIgnored := ignores[id] + + ignored := generalIgnored || ruleIgnored + suppressions := append(generalSuppressions, ruleSuppressions...) + + // Track external suppressions. + if gosec.ruleset.IsRuleSuppressed(id) { + ignored = true + suppressions = append(suppressions, issue.SuppressionInfo{ + Kind: "external", + Justification: externalSuppressionJustification, + }) + } + return suppressions, ignored +} + +func (gosec *Analyzer) updateIssues(issue *issue.Issue, ignored bool, suppressions []issue.SuppressionInfo) { + if issue != nil { + if gosec.showIgnored { + issue.NoSec = ignored } - if issue != nil { - if gosec.showIgnored { - issue.NoSec = ignored - } - if !ignored || !gosec.showIgnored { - gosec.stats.NumFound++ - } - if ignored && gosec.trackSuppressions { - issue.WithSuppressions(suppressions) - gosec.issues = append(gosec.issues, issue) - } else if !ignored || gosec.showIgnored || gosec.ignoreNosec { - gosec.issues = append(gosec.issues, issue) - } + if !ignored || !gosec.showIgnored { + gosec.stats.NumFound++ + } + if ignored && gosec.trackSuppressions { + issue.WithSuppressions(suppressions) + gosec.issues = append(gosec.issues, issue) + } else if !ignored || gosec.showIgnored || gosec.ignoreNosec { + gosec.issues = append(gosec.issues, issue) } } - return gosec } // Report returns the current issues discovered and the metrics about the scan -func (gosec *Analyzer) Report() ([]*Issue, *Metrics, map[string][]Error) { +func (gosec *Analyzer) Report() ([]*issue.Issue, *Metrics, map[string][]Error) { return gosec.issues, gosec.stats, gosec.errors } // Reset clears state such as context, issues and metrics from the configured analyzer func (gosec *Analyzer) Reset() { gosec.context = &Context{} - gosec.issues = make([]*Issue, 0, 16) + gosec.issues = make([]*issue.Issue, 0, 16) gosec.stats = &Metrics{} gosec.ruleset = NewRuleSet() } diff --git a/vendor/github.com/securego/gosec/v2/analyzers/ssrf.go b/vendor/github.com/securego/gosec/v2/analyzers/ssrf.go new file mode 100644 index 00000000..a9dbd950 --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/analyzers/ssrf.go @@ -0,0 +1,57 @@ +// (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" + "golang.org/x/tools/go/analysis/passes/buildssa" + "golang.org/x/tools/go/ssa" + + "github.com/securego/gosec/v2/issue" +) + +func newSSRFAnalyzer(id string, description string) *analysis.Analyzer { + return &analysis.Analyzer{ + Name: id, + Doc: description, + Run: runSSRF, + Requires: []*analysis.Analyzer{buildssa.Analyzer}, + } +} + +func runSSRF(pass *analysis.Pass) (interface{}, error) { + ssaResult, err := getSSAResult(pass) + if err != nil { + return nil, err + } + // TODO: implement the analysis + for _, fn := range ssaResult.SSA.SrcFuncs { + for _, block := range fn.DomPreorder() { + for _, instr := range block.Instrs { + switch instr := instr.(type) { + case *ssa.Call: + callee := instr.Call.StaticCallee() + if callee != nil { + ssaResult.Logger.Printf("callee: %s\n", callee) + return newIssue(pass.Analyzer.Name, + "not implemeted", + pass.Fset, instr.Call.Pos(), issue.Low, issue.High), nil + } + } + } + } + } + return nil, nil +} diff --git a/vendor/github.com/securego/gosec/v2/analyzers/util.go b/vendor/github.com/securego/gosec/v2/analyzers/util.go new file mode 100644 index 00000000..b090a3e4 --- /dev/null +++ b/vendor/github.com/securego/gosec/v2/analyzers/util.go @@ -0,0 +1,98 @@ +// (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" + "log" + "os" + "strconv" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/buildssa" + + "github.com/securego/gosec/v2/issue" +) + +// SSAAnalyzerResult contains various information returned by the +// SSA analysis along with some configuraion +type SSAAnalyzerResult struct { + Config map[string]interface{} + Logger *log.Logger + SSA *buildssa.SSA +} + +// BuildDefaultAnalyzers returns the default list of analyzers +func BuildDefaultAnalyzers() []*analysis.Analyzer { + return []*analysis.Analyzer{ + newSSRFAnalyzer("G107", "URL provided to HTTP request as taint input"), + } +} + +// getSSAResult retrives the SSA result from analysis pass +func getSSAResult(pass *analysis.Pass) (*SSAAnalyzerResult, error) { + result, ok := pass.ResultOf[buildssa.Analyzer] + if !ok { + return nil, fmt.Errorf("no SSA result found in the analysis pass") + } + ssaResult, ok := result.(*SSAAnalyzerResult) + if !ok { + return nil, fmt.Errorf("the analysis pass result is not of type SSA") + } + return ssaResult, nil +} + +// newIssue creates a new gosec issue +func newIssue(analyzerID string, desc string, fileSet *token.FileSet, + pos token.Pos, severity, confidence issue.Score, +) *issue.Issue { + file := fileSet.File(pos) + line := file.Line(pos) + col := file.Position(pos).Column + + return &issue.Issue{ + RuleID: analyzerID, + File: file.Name(), + Line: strconv.Itoa(line), + Col: strconv.Itoa(col), + Severity: severity, + Confidence: confidence, + What: desc, + Cwe: issue.GetCweByRule(analyzerID), + Code: issueCodeSnippet(fileSet, pos), + } +} + +func issueCodeSnippet(fileSet *token.FileSet, pos token.Pos) string { + file := fileSet.File(pos) + + start := (int64)(file.Line(pos)) + if start-issue.SnippetOffset > 0 { + start = start - issue.SnippetOffset + } + end := (int64)(file.Line(pos)) + end = end + issue.SnippetOffset + + var code string + if file, err := os.Open(file.Name()); err == nil { + defer file.Close() // #nosec + code, err = issue.CodeSnippet(file, start, end) + if err != nil { + return err.Error() + } + } + return code +} diff --git a/vendor/github.com/securego/gosec/v2/config.go b/vendor/github.com/securego/gosec/v2/config.go index 443d45f7..ca4cf217 100644 --- a/vendor/github.com/securego/gosec/v2/config.go +++ b/vendor/github.com/securego/gosec/v2/config.go @@ -29,6 +29,8 @@ const ( ExcludeRules GlobalOption = "exclude" // IncludeRules global option for should be load IncludeRules GlobalOption = "include" + // SSA global option to enable go analysis framework with SSA support + SSA GlobalOption = "ssa" ) // Config is used to provide configuration and customization to each of the rules. diff --git a/vendor/github.com/securego/gosec/v2/helpers.go b/vendor/github.com/securego/gosec/v2/helpers.go index 62ede054..08b7893e 100644 --- a/vendor/github.com/securego/gosec/v2/helpers.go +++ b/vendor/github.com/securego/gosec/v2/helpers.go @@ -182,7 +182,7 @@ func GetCallInfo(n ast.Node, ctx *Context) (string, string, error) { } // GetCallStringArgsValues returns the values of strings arguments if they can be resolved -func GetCallStringArgsValues(n ast.Node, ctx *Context) []string { +func GetCallStringArgsValues(n ast.Node, _ *Context) []string { values := []string{} switch node := n.(type) { case *ast.CallExpr: diff --git a/vendor/github.com/securego/gosec/v2/import_tracker.go b/vendor/github.com/securego/gosec/v2/import_tracker.go index 30e7c009..7984e99f 100644 --- a/vendor/github.com/securego/gosec/v2/import_tracker.go +++ b/vendor/github.com/securego/gosec/v2/import_tracker.go @@ -51,9 +51,7 @@ func (t *ImportTracker) TrackPackages(pkgs ...*types.Package) { func (t *ImportTracker) TrackImport(imported *ast.ImportSpec) { importPath := strings.Trim(imported.Path.Value, `"`) if imported.Name != nil { - if imported.Name.Name == "_" { - // Initialization only import - } else { + if imported.Name.Name != "_" { // Aliased import t.Imported[importPath] = append(t.Imported[importPath], imported.Name.String()) } diff --git a/vendor/github.com/securego/gosec/v2/issue.go b/vendor/github.com/securego/gosec/v2/issue/issue.go similarity index 87% rename from vendor/github.com/securego/gosec/v2/issue.go rename to vendor/github.com/securego/gosec/v2/issue/issue.go index d8faf4bf..5bf00dec 100644 --- a/vendor/github.com/securego/gosec/v2/issue.go +++ b/vendor/github.com/securego/gosec/v2/issue/issue.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package gosec +package issue import ( "bufio" @@ -77,7 +77,6 @@ var ruleToCWE = map[string]string{ "G304": "22", "G305": "22", "G306": "276", - "G307": "703", "G401": "326", "G402": "295", "G403": "310", @@ -105,8 +104,15 @@ type Issue struct { Suppressions []SuppressionInfo `json:"suppressions"` // Suppression info of the issue } +// SuppressionInfo object is to record the kind and the justification that used +// to suppress violations. +type SuppressionInfo struct { + Kind string `json:"kind"` + Justification string `json:"justification"` +} + // FileLocation point out the file path and line number in file -func (i Issue) FileLocation() string { +func (i *Issue) FileLocation() string { return fmt.Sprintf("%s:%s", i.File, i.Line) } @@ -137,11 +143,8 @@ func (c Score) String() string { return "UNDEFINED" } -// codeSnippet extracts a code snippet based on the ast reference -func codeSnippet(file *os.File, start int64, end int64, n ast.Node) (string, error) { - if n == nil { - return "", fmt.Errorf("invalid AST node provided") - } +// CodeSnippet extracts a code snippet based on the ast reference +func CodeSnippet(file *os.File, start int64, end int64) (string, error) { var pos int64 var buf bytes.Buffer scanner := bufio.NewScanner(file) @@ -171,9 +174,8 @@ func codeSnippetEndLine(node ast.Node, fobj *token.File) int64 { return e + SnippetOffset } -// NewIssue creates a new Issue -func NewIssue(ctx *Context, node ast.Node, ruleID, desc string, severity Score, confidence Score) *Issue { - fobj := ctx.FileSet.File(node.Pos()) +// New creates a new Issue +func New(fobj *token.File, node ast.Node, ruleID, desc string, severity, confidence Score) *Issue { name := fobj.Name() start, end := fobj.Line(node.Pos()), fobj.Line(node.End()) line := strconv.Itoa(start) @@ -183,11 +185,14 @@ func NewIssue(ctx *Context, node ast.Node, ruleID, desc string, severity Score, col := strconv.Itoa(fobj.Position(node.Pos()).Column) var code string - if file, err := os.Open(fobj.Name()); err == nil { + if node == nil { + code = "invalid AST node provided" + } + if file, err := os.Open(fobj.Name()); err == nil && node != nil { defer file.Close() // #nosec s := codeSnippetStartLine(node, fobj) e := codeSnippetEndLine(node, fobj) - code, err = codeSnippet(file, s, e, node) + code, err = CodeSnippet(file, s, e) if err != nil { code = err.Error() } diff --git a/vendor/github.com/securego/gosec/v2/report.go b/vendor/github.com/securego/gosec/v2/report.go index 96b1466d..4fdeea52 100644 --- a/vendor/github.com/securego/gosec/v2/report.go +++ b/vendor/github.com/securego/gosec/v2/report.go @@ -1,15 +1,19 @@ package gosec +import ( + "github.com/securego/gosec/v2/issue" +) + // ReportInfo this is report information type ReportInfo struct { Errors map[string][]Error `json:"Golang errors"` - Issues []*Issue + Issues []*issue.Issue Stats *Metrics GosecVersion string } // NewReportInfo instantiate a ReportInfo -func NewReportInfo(issues []*Issue, metrics *Metrics, errors map[string][]Error) *ReportInfo { +func NewReportInfo(issues []*issue.Issue, metrics *Metrics, errors map[string][]Error) *ReportInfo { return &ReportInfo{ Errors: errors, Issues: issues, diff --git a/vendor/github.com/securego/gosec/v2/resolve.go b/vendor/github.com/securego/gosec/v2/resolve.go index cdc287e8..a201b8d3 100644 --- a/vendor/github.com/securego/gosec/v2/resolve.go +++ b/vendor/github.com/securego/gosec/v2/resolve.go @@ -66,7 +66,7 @@ func resolveBinExpr(n *ast.BinaryExpr, c *Context) bool { return (TryResolve(n.X, c) && TryResolve(n.Y, c)) } -func resolveCallExpr(n *ast.CallExpr, c *Context) bool { +func resolveCallExpr(_ *ast.CallExpr, _ *Context) bool { // TODO(tkelsey): next step, full function resolution return false } diff --git a/vendor/github.com/securego/gosec/v2/rule.go b/vendor/github.com/securego/gosec/v2/rule.go index c0429c4c..5e973b6a 100644 --- a/vendor/github.com/securego/gosec/v2/rule.go +++ b/vendor/github.com/securego/gosec/v2/rule.go @@ -15,12 +15,14 @@ package gosec import ( "go/ast" "reflect" + + "github.com/securego/gosec/v2/issue" ) // The Rule interface used by all rules supported by gosec. type Rule interface { ID() string - Match(ast.Node, *Context) (*Issue, error) + Match(ast.Node, *Context) (*issue.Issue, error) } // RuleBuilder is used to register a rule definition with the analyzer diff --git a/vendor/github.com/securego/gosec/v2/rules/archive.go b/vendor/github.com/securego/gosec/v2/rules/archive.go index 92c7e448..98704743 100644 --- a/vendor/github.com/securego/gosec/v2/rules/archive.go +++ b/vendor/github.com/securego/gosec/v2/rules/archive.go @@ -5,10 +5,11 @@ import ( "go/types" "github.com/securego/gosec/v2" + "github.com/securego/gosec/v2/issue" ) type archive struct { - gosec.MetaData + issue.MetaData calls gosec.CallList argTypes []string } @@ -18,7 +19,7 @@ func (a *archive) ID() string { } // Match inspects AST nodes to determine if the filepath.Joins uses any argument derived from type zip.File or tar.Header -func (a *archive) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { +func (a *archive) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) { if node := a.calls.ContainsPkgCallExpr(n, c, false); node != nil { for _, arg := range node.Args { var argType types.Type @@ -38,7 +39,7 @@ func (a *archive) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { if argType != nil { for _, t := range a.argTypes { if argType.String() == t { - return gosec.NewIssue(c, n, a.ID(), a.What, a.Severity, a.Confidence), nil + return c.NewIssue(n, a.ID(), a.What, a.Severity, a.Confidence), nil } } } @@ -48,17 +49,17 @@ func (a *archive) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { } // NewArchive creates a new rule which detects the file traversal when extracting zip/tar archives -func NewArchive(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { +func NewArchive(id string, _ gosec.Config) (gosec.Rule, []ast.Node) { calls := gosec.NewCallList() calls.Add("path/filepath", "Join") calls.Add("path", "Join") return &archive{ calls: calls, argTypes: []string{"*archive/zip.File", "*archive/tar.Header"}, - MetaData: gosec.MetaData{ + MetaData: issue.MetaData{ ID: id, - Severity: gosec.Medium, - Confidence: gosec.High, + Severity: issue.Medium, + Confidence: issue.High, What: "File traversal when extracting zip/tar archive", }, }, []ast.Node{(*ast.CallExpr)(nil)} diff --git a/vendor/github.com/securego/gosec/v2/rules/bad_defer.go b/vendor/github.com/securego/gosec/v2/rules/bad_defer.go deleted file mode 100644 index 141a4a93..00000000 --- a/vendor/github.com/securego/gosec/v2/rules/bad_defer.go +++ /dev/null @@ -1,96 +0,0 @@ -package rules - -import ( - "fmt" - "go/ast" - "strings" - - "github.com/securego/gosec/v2" -) - -type deferType struct { - typ string - methods []string -} - -type badDefer struct { - gosec.MetaData - types []deferType -} - -func (r *badDefer) ID() string { - return r.MetaData.ID -} - -func normalize(typ string) string { - return strings.TrimPrefix(typ, "*") -} - -func contains(methods []string, method string) bool { - for _, m := range methods { - if m == method { - return true - } - } - return false -} - -func (r *badDefer) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { - if deferStmt, ok := n.(*ast.DeferStmt); ok { - for _, deferTyp := range r.types { - if typ, method, err := gosec.GetCallInfo(deferStmt.Call, c); err == nil { - if normalize(typ) == deferTyp.typ && contains(deferTyp.methods, method) { - return gosec.NewIssue(c, n, r.ID(), fmt.Sprintf(r.What, method, typ), r.Severity, r.Confidence), nil - } - } - } - } - - return nil, nil -} - -// NewDeferredClosing detects unsafe defer of error returning methods -func NewDeferredClosing(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { - return &badDefer{ - types: []deferType{ - { - typ: "os.File", - methods: []string{"Close"}, - }, - { - typ: "io.ReadCloser", - methods: []string{"Close"}, - }, - { - typ: "io.WriteCloser", - methods: []string{"Close"}, - }, - { - typ: "io.ReadWriteCloser", - methods: []string{"Close"}, - }, - { - typ: "io.ReadSeekCloser", - methods: []string{"Close"}, - }, - { - typ: "io.Closer", - methods: []string{"Close"}, - }, - { - typ: "net.Conn", - methods: []string{"Close"}, - }, - { - typ: "net.Listener", - methods: []string{"Close"}, - }, - }, - MetaData: gosec.MetaData{ - ID: id, - Severity: gosec.Medium, - Confidence: gosec.High, - What: "Deferring unsafe method %q on type %q", - }, - }, []ast.Node{(*ast.DeferStmt)(nil)} -} diff --git a/vendor/github.com/securego/gosec/v2/rules/bind.go b/vendor/github.com/securego/gosec/v2/rules/bind.go index 8f6af067..fef760c8 100644 --- a/vendor/github.com/securego/gosec/v2/rules/bind.go +++ b/vendor/github.com/securego/gosec/v2/rules/bind.go @@ -19,11 +19,12 @@ import ( "regexp" "github.com/securego/gosec/v2" + "github.com/securego/gosec/v2/issue" ) // Looks for net.Listen("0.0.0.0") or net.Listen(":8080") type bindsToAllNetworkInterfaces struct { - gosec.MetaData + issue.MetaData calls gosec.CallList pattern *regexp.Regexp } @@ -32,7 +33,7 @@ func (r *bindsToAllNetworkInterfaces) ID() string { return r.MetaData.ID } -func (r *bindsToAllNetworkInterfaces) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { +func (r *bindsToAllNetworkInterfaces) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) { callExpr := r.calls.ContainsPkgCallExpr(n, c, false) if callExpr == nil { return nil, nil @@ -42,14 +43,14 @@ func (r *bindsToAllNetworkInterfaces) Match(n ast.Node, c *gosec.Context) (*gose if bl, ok := arg.(*ast.BasicLit); ok { if arg, err := gosec.GetString(bl); err == nil { if r.pattern.MatchString(arg) { - return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil + return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil } } } else if ident, ok := arg.(*ast.Ident); ok { values := gosec.GetIdentStringValues(ident) for _, value := range values { if r.pattern.MatchString(value) { - return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil + return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil } } } @@ -57,7 +58,7 @@ func (r *bindsToAllNetworkInterfaces) Match(n ast.Node, c *gosec.Context) (*gose values := gosec.GetCallStringArgsValues(callExpr.Args[0], c) for _, value := range values { if r.pattern.MatchString(value) { - return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil + return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil } } } @@ -66,17 +67,17 @@ func (r *bindsToAllNetworkInterfaces) Match(n ast.Node, c *gosec.Context) (*gose // NewBindsToAllNetworkInterfaces detects socket connections that are setup to // listen on all network interfaces. -func NewBindsToAllNetworkInterfaces(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { +func NewBindsToAllNetworkInterfaces(id string, _ gosec.Config) (gosec.Rule, []ast.Node) { calls := gosec.NewCallList() calls.Add("net", "Listen") calls.Add("crypto/tls", "Listen") return &bindsToAllNetworkInterfaces{ calls: calls, pattern: regexp.MustCompile(`^(0.0.0.0|:).*$`), - MetaData: gosec.MetaData{ + MetaData: issue.MetaData{ ID: id, - Severity: gosec.Medium, - Confidence: gosec.High, + Severity: issue.Medium, + Confidence: issue.High, What: "Binds to all network interfaces", }, }, []ast.Node{(*ast.CallExpr)(nil)} diff --git a/vendor/github.com/securego/gosec/v2/rules/blocklist.go b/vendor/github.com/securego/gosec/v2/rules/blocklist.go index afd4ee56..5e03cf7a 100644 --- a/vendor/github.com/securego/gosec/v2/rules/blocklist.go +++ b/vendor/github.com/securego/gosec/v2/rules/blocklist.go @@ -19,27 +19,28 @@ import ( "strings" "github.com/securego/gosec/v2" + "github.com/securego/gosec/v2/issue" ) type blocklistedImport struct { - gosec.MetaData + issue.MetaData Blocklisted map[string]string } func unquote(original string) string { - copy := strings.TrimSpace(original) - copy = strings.TrimLeft(copy, `"`) - return strings.TrimRight(copy, `"`) + cleaned := strings.TrimSpace(original) + cleaned = strings.TrimLeft(cleaned, `"`) + return strings.TrimRight(cleaned, `"`) } func (r *blocklistedImport) ID() string { return r.MetaData.ID } -func (r *blocklistedImport) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { +func (r *blocklistedImport) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) { if node, ok := n.(*ast.ImportSpec); ok { if description, ok := r.Blocklisted[unquote(node.Path.Value)]; ok { - return gosec.NewIssue(c, node, r.ID(), description, r.Severity, r.Confidence), nil + return c.NewIssue(node, r.ID(), description, r.Severity, r.Confidence), nil } } return nil, nil @@ -47,12 +48,12 @@ func (r *blocklistedImport) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, e // NewBlocklistedImports reports when a blocklisted import is being used. // Typically when a deprecated technology is being used. -func NewBlocklistedImports(id string, conf gosec.Config, blocklist map[string]string) (gosec.Rule, []ast.Node) { +func NewBlocklistedImports(id string, _ gosec.Config, blocklist map[string]string) (gosec.Rule, []ast.Node) { return &blocklistedImport{ - MetaData: gosec.MetaData{ + MetaData: issue.MetaData{ ID: id, - Severity: gosec.Medium, - Confidence: gosec.High, + Severity: issue.Medium, + Confidence: issue.High, }, Blocklisted: blocklist, }, []ast.Node{(*ast.ImportSpec)(nil)} diff --git a/vendor/github.com/securego/gosec/v2/rules/decompression-bomb.go b/vendor/github.com/securego/gosec/v2/rules/decompression-bomb.go index 02256faa..7e57f1a5 100644 --- a/vendor/github.com/securego/gosec/v2/rules/decompression-bomb.go +++ b/vendor/github.com/securego/gosec/v2/rules/decompression-bomb.go @@ -19,10 +19,11 @@ import ( "go/ast" "github.com/securego/gosec/v2" + "github.com/securego/gosec/v2/issue" ) type decompressionBombCheck struct { - gosec.MetaData + issue.MetaData readerCalls gosec.CallList copyCalls gosec.CallList } @@ -40,7 +41,7 @@ func containsReaderCall(node ast.Node, ctx *gosec.Context, list gosec.CallList) return list.Contains(s, idt) } -func (d *decompressionBombCheck) Match(node ast.Node, ctx *gosec.Context) (*gosec.Issue, error) { +func (d *decompressionBombCheck) Match(node ast.Node, ctx *gosec.Context) (*issue.Issue, error) { var readerVarObj map[*ast.Object]struct{} // To check multiple lines, ctx.PassedValues is used to store temporary data. @@ -72,7 +73,7 @@ func (d *decompressionBombCheck) Match(node ast.Node, ctx *gosec.Context) (*gose if idt, ok := n.Args[1].(*ast.Ident); ok { if _, ok := readerVarObj[idt.Obj]; ok { // Detect io.Copy(x, r) - return gosec.NewIssue(ctx, n, d.ID(), d.What, d.Severity, d.Confidence), nil + return ctx.NewIssue(n, d.ID(), d.What, d.Severity, d.Confidence), nil } } } @@ -82,7 +83,7 @@ func (d *decompressionBombCheck) Match(node ast.Node, ctx *gosec.Context) (*gose } // NewDecompressionBombCheck detects if there is potential DoS vulnerability via decompression bomb -func NewDecompressionBombCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { +func NewDecompressionBombCheck(id string, _ gosec.Config) (gosec.Rule, []ast.Node) { readerCalls := gosec.NewCallList() readerCalls.Add("compress/gzip", "NewReader") readerCalls.AddAll("compress/zlib", "NewReader", "NewReaderDict") @@ -98,10 +99,10 @@ func NewDecompressionBombCheck(id string, conf gosec.Config) (gosec.Rule, []ast. copyCalls.Add("io", "CopyBuffer") return &decompressionBombCheck{ - MetaData: gosec.MetaData{ + MetaData: issue.MetaData{ ID: id, - Severity: gosec.Medium, - Confidence: gosec.Medium, + Severity: issue.Medium, + Confidence: issue.Medium, What: "Potential DoS vulnerability via decompression bomb", }, readerCalls: readerCalls, diff --git a/vendor/github.com/securego/gosec/v2/rules/directory-traversal.go b/vendor/github.com/securego/gosec/v2/rules/directory-traversal.go index c373427b..47bcb2dc 100644 --- a/vendor/github.com/securego/gosec/v2/rules/directory-traversal.go +++ b/vendor/github.com/securego/gosec/v2/rules/directory-traversal.go @@ -5,18 +5,19 @@ import ( "regexp" "github.com/securego/gosec/v2" + "github.com/securego/gosec/v2/issue" ) type traversal struct { pattern *regexp.Regexp - gosec.MetaData + issue.MetaData } func (r *traversal) ID() string { return r.MetaData.ID } -func (r *traversal) Match(n ast.Node, ctx *gosec.Context) (*gosec.Issue, error) { +func (r *traversal) Match(n ast.Node, ctx *gosec.Context) (*issue.Issue, error) { switch node := n.(type) { case *ast.CallExpr: return r.matchCallExpr(node, ctx) @@ -24,14 +25,14 @@ func (r *traversal) Match(n ast.Node, ctx *gosec.Context) (*gosec.Issue, error) return nil, nil } -func (r *traversal) matchCallExpr(assign *ast.CallExpr, ctx *gosec.Context) (*gosec.Issue, error) { +func (r *traversal) matchCallExpr(assign *ast.CallExpr, ctx *gosec.Context) (*issue.Issue, error) { for _, i := range assign.Args { if basiclit, ok1 := i.(*ast.BasicLit); ok1 { if fun, ok2 := assign.Fun.(*ast.SelectorExpr); ok2 { if x, ok3 := fun.X.(*ast.Ident); ok3 { - string := x.Name + "." + fun.Sel.Name + "(" + basiclit.Value + ")" - if r.pattern.MatchString(string) { - return gosec.NewIssue(ctx, assign, r.ID(), r.What, r.Severity, r.Confidence), nil + str := x.Name + "." + fun.Sel.Name + "(" + basiclit.Value + ")" + if r.pattern.MatchString(str) { + return ctx.NewIssue(assign, r.ID(), r.What, r.Severity, r.Confidence), nil } } } @@ -54,11 +55,11 @@ func NewDirectoryTraversal(id string, conf gosec.Config) (gosec.Rule, []ast.Node return &traversal{ pattern: regexp.MustCompile(pattern), - MetaData: gosec.MetaData{ + MetaData: issue.MetaData{ ID: id, What: "Potential directory traversal", - Confidence: gosec.Medium, - Severity: gosec.Medium, + Confidence: issue.Medium, + Severity: issue.Medium, }, }, []ast.Node{(*ast.CallExpr)(nil)} } diff --git a/vendor/github.com/securego/gosec/v2/rules/errors.go b/vendor/github.com/securego/gosec/v2/rules/errors.go index 0838382b..d31248cc 100644 --- a/vendor/github.com/securego/gosec/v2/rules/errors.go +++ b/vendor/github.com/securego/gosec/v2/rules/errors.go @@ -19,10 +19,11 @@ import ( "go/types" "github.com/securego/gosec/v2" + "github.com/securego/gosec/v2/issue" ) type noErrorCheck struct { - gosec.MetaData + issue.MetaData whitelist gosec.CallList } @@ -49,7 +50,7 @@ func returnsError(callExpr *ast.CallExpr, ctx *gosec.Context) int { return -1 } -func (r *noErrorCheck) Match(n ast.Node, ctx *gosec.Context) (*gosec.Issue, error) { +func (r *noErrorCheck) Match(n ast.Node, ctx *gosec.Context) (*issue.Issue, error) { switch stmt := n.(type) { case *ast.AssignStmt: cfg := ctx.Config @@ -61,7 +62,7 @@ func (r *noErrorCheck) Match(n ast.Node, ctx *gosec.Context) (*gosec.Issue, erro return nil, nil } if id, ok := stmt.Lhs[pos].(*ast.Ident); ok && id.Name == "_" { - return gosec.NewIssue(ctx, n, r.ID(), r.What, r.Severity, r.Confidence), nil + return ctx.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil } } } @@ -70,7 +71,7 @@ func (r *noErrorCheck) Match(n ast.Node, ctx *gosec.Context) (*gosec.Issue, erro if callExpr, ok := stmt.X.(*ast.CallExpr); ok && r.whitelist.ContainsCallExpr(stmt.X, ctx) == nil { pos := returnsError(callExpr, ctx) if pos >= 0 { - return gosec.NewIssue(ctx, n, r.ID(), r.What, r.Severity, r.Confidence), nil + return ctx.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil } } } @@ -100,10 +101,10 @@ func NewNoErrorCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { } return &noErrorCheck{ - MetaData: gosec.MetaData{ + MetaData: issue.MetaData{ ID: id, - Severity: gosec.Low, - Confidence: gosec.High, + Severity: issue.Low, + Confidence: issue.High, What: "Errors unhandled.", }, whitelist: whitelist, diff --git a/vendor/github.com/securego/gosec/v2/rules/fileperms.go b/vendor/github.com/securego/gosec/v2/rules/fileperms.go index e89b5636..0376b6a0 100644 --- a/vendor/github.com/securego/gosec/v2/rules/fileperms.go +++ b/vendor/github.com/securego/gosec/v2/rules/fileperms.go @@ -20,10 +20,11 @@ import ( "strconv" "github.com/securego/gosec/v2" + "github.com/securego/gosec/v2/issue" ) type filePermissions struct { - gosec.MetaData + issue.MetaData mode int64 pkgs []string calls []string @@ -54,12 +55,12 @@ func modeIsSubset(subset int64, superset int64) bool { return (subset | superset) == superset } -func (r *filePermissions) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { +func (r *filePermissions) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) { for _, pkg := range r.pkgs { if callexpr, matched := gosec.MatchCallByPackage(n, c, pkg, r.calls...); matched { modeArg := callexpr.Args[len(callexpr.Args)-1] if mode, err := gosec.GetInt(modeArg); err == nil && !modeIsSubset(mode, r.mode) { - return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil + return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil } } } @@ -73,10 +74,10 @@ func NewWritePerms(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { mode: mode, pkgs: []string{"io/ioutil", "os"}, calls: []string{"WriteFile"}, - MetaData: gosec.MetaData{ + MetaData: issue.MetaData{ ID: id, - Severity: gosec.Medium, - Confidence: gosec.High, + Severity: issue.Medium, + Confidence: issue.High, What: fmt.Sprintf("Expect WriteFile permissions to be %#o or less", mode), }, }, []ast.Node{(*ast.CallExpr)(nil)} @@ -90,10 +91,10 @@ func NewFilePerms(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { mode: mode, pkgs: []string{"os"}, calls: []string{"OpenFile", "Chmod"}, - MetaData: gosec.MetaData{ + MetaData: issue.MetaData{ ID: id, - Severity: gosec.Medium, - Confidence: gosec.High, + Severity: issue.Medium, + Confidence: issue.High, What: fmt.Sprintf("Expect file permissions to be %#o or less", mode), }, }, []ast.Node{(*ast.CallExpr)(nil)} @@ -107,10 +108,10 @@ func NewMkdirPerms(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { mode: mode, pkgs: []string{"os"}, calls: []string{"Mkdir", "MkdirAll"}, - MetaData: gosec.MetaData{ + MetaData: issue.MetaData{ ID: id, - Severity: gosec.Medium, - Confidence: gosec.High, + Severity: issue.Medium, + Confidence: issue.High, What: fmt.Sprintf("Expect directory permissions to be %#o or less", mode), }, }, []ast.Node{(*ast.CallExpr)(nil)} diff --git a/vendor/github.com/securego/gosec/v2/rules/hardcoded_credentials.go b/vendor/github.com/securego/gosec/v2/rules/hardcoded_credentials.go index b9e57565..eac50d7c 100644 --- a/vendor/github.com/securego/gosec/v2/rules/hardcoded_credentials.go +++ b/vendor/github.com/securego/gosec/v2/rules/hardcoded_credentials.go @@ -21,11 +21,13 @@ import ( "strconv" zxcvbn "github.com/nbutton23/zxcvbn-go" + "github.com/securego/gosec/v2" + "github.com/securego/gosec/v2/issue" ) type credentials struct { - gosec.MetaData + issue.MetaData pattern *regexp.Regexp entropyThreshold float64 perCharThreshold float64 @@ -53,7 +55,7 @@ func (r *credentials) isHighEntropyString(str string) bool { entropyPerChar >= r.perCharThreshold)) } -func (r *credentials) Match(n ast.Node, ctx *gosec.Context) (*gosec.Issue, error) { +func (r *credentials) Match(n ast.Node, ctx *gosec.Context) (*issue.Issue, error) { switch node := n.(type) { case *ast.AssignStmt: return r.matchAssign(node, ctx) @@ -65,14 +67,14 @@ func (r *credentials) Match(n ast.Node, ctx *gosec.Context) (*gosec.Issue, error return nil, nil } -func (r *credentials) matchAssign(assign *ast.AssignStmt, ctx *gosec.Context) (*gosec.Issue, error) { +func (r *credentials) matchAssign(assign *ast.AssignStmt, ctx *gosec.Context) (*issue.Issue, error) { for _, i := range assign.Lhs { if ident, ok := i.(*ast.Ident); ok { if r.pattern.MatchString(ident.Name) { for _, e := range assign.Rhs { if val, err := gosec.GetString(e); err == nil { if r.ignoreEntropy || (!r.ignoreEntropy && r.isHighEntropyString(val)) { - return gosec.NewIssue(ctx, assign, r.ID(), r.What, r.Severity, r.Confidence), nil + return ctx.NewIssue(assign, r.ID(), r.What, r.Severity, r.Confidence), nil } } } @@ -82,7 +84,7 @@ func (r *credentials) matchAssign(assign *ast.AssignStmt, ctx *gosec.Context) (* return nil, nil } -func (r *credentials) matchValueSpec(valueSpec *ast.ValueSpec, ctx *gosec.Context) (*gosec.Issue, error) { +func (r *credentials) matchValueSpec(valueSpec *ast.ValueSpec, ctx *gosec.Context) (*issue.Issue, error) { for index, ident := range valueSpec.Names { if r.pattern.MatchString(ident.Name) && valueSpec.Values != nil { // const foo, bar = "same value" @@ -91,7 +93,7 @@ func (r *credentials) matchValueSpec(valueSpec *ast.ValueSpec, ctx *gosec.Contex } if val, err := gosec.GetString(valueSpec.Values[index]); err == nil { if r.ignoreEntropy || (!r.ignoreEntropy && r.isHighEntropyString(val)) { - return gosec.NewIssue(ctx, valueSpec, r.ID(), r.What, r.Severity, r.Confidence), nil + return ctx.NewIssue(valueSpec, r.ID(), r.What, r.Severity, r.Confidence), nil } } } @@ -99,7 +101,7 @@ func (r *credentials) matchValueSpec(valueSpec *ast.ValueSpec, ctx *gosec.Contex return nil, nil } -func (r *credentials) matchEqualityCheck(binaryExpr *ast.BinaryExpr, ctx *gosec.Context) (*gosec.Issue, error) { +func (r *credentials) matchEqualityCheck(binaryExpr *ast.BinaryExpr, ctx *gosec.Context) (*issue.Issue, error) { if binaryExpr.Op == token.EQL || binaryExpr.Op == token.NEQ { ident, ok := binaryExpr.X.(*ast.Ident) if !ok { @@ -113,7 +115,7 @@ func (r *credentials) matchEqualityCheck(binaryExpr *ast.BinaryExpr, ctx *gosec. } if val, err := gosec.GetString(valueNode); err == nil { if r.ignoreEntropy || (!r.ignoreEntropy && r.isHighEntropyString(val)) { - return gosec.NewIssue(ctx, binaryExpr, r.ID(), r.What, r.Severity, r.Confidence), nil + return ctx.NewIssue(binaryExpr, r.ID(), r.What, r.Severity, r.Confidence), nil } } } @@ -170,11 +172,11 @@ func NewHardcodedCredentials(id string, conf gosec.Config) (gosec.Rule, []ast.No perCharThreshold: perCharThreshold, ignoreEntropy: ignoreEntropy, truncate: truncateString, - MetaData: gosec.MetaData{ + MetaData: issue.MetaData{ ID: id, What: "Potential hardcoded credentials", - Confidence: gosec.Low, - Severity: gosec.High, + Confidence: issue.Low, + Severity: issue.High, }, }, []ast.Node{(*ast.AssignStmt)(nil), (*ast.ValueSpec)(nil), (*ast.BinaryExpr)(nil)} } diff --git a/vendor/github.com/securego/gosec/v2/rules/http_serve.go b/vendor/github.com/securego/gosec/v2/rules/http_serve.go index e460b3a6..525ed4eb 100644 --- a/vendor/github.com/securego/gosec/v2/rules/http_serve.go +++ b/vendor/github.com/securego/gosec/v2/rules/http_serve.go @@ -4,10 +4,11 @@ import ( "go/ast" "github.com/securego/gosec/v2" + "github.com/securego/gosec/v2/issue" ) type httpServeWithoutTimeouts struct { - gosec.MetaData + issue.MetaData pkg string calls []string } @@ -16,23 +17,23 @@ func (r *httpServeWithoutTimeouts) ID() string { return r.MetaData.ID } -func (r *httpServeWithoutTimeouts) Match(n ast.Node, c *gosec.Context) (gi *gosec.Issue, err error) { +func (r *httpServeWithoutTimeouts) Match(n ast.Node, c *gosec.Context) (gi *issue.Issue, err error) { if _, matches := gosec.MatchCallByPackage(n, c, r.pkg, r.calls...); matches { - return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil + return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil } return nil, nil } // NewHTTPServeWithoutTimeouts detects use of net/http serve functions that have no support for setting timeouts. -func NewHTTPServeWithoutTimeouts(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { +func NewHTTPServeWithoutTimeouts(id string, _ gosec.Config) (gosec.Rule, []ast.Node) { return &httpServeWithoutTimeouts{ pkg: "net/http", calls: []string{"ListenAndServe", "ListenAndServeTLS", "Serve", "ServeTLS"}, - MetaData: gosec.MetaData{ + MetaData: issue.MetaData{ ID: id, What: "Use of net/http serve function that has no support for setting timeouts", - Severity: gosec.Medium, - Confidence: gosec.High, + Severity: issue.Medium, + Confidence: issue.High, }, }, []ast.Node{(*ast.CallExpr)(nil)} } diff --git a/vendor/github.com/securego/gosec/v2/rules/implicit_aliasing.go b/vendor/github.com/securego/gosec/v2/rules/implicit_aliasing.go index b2668dec..70678e29 100644 --- a/vendor/github.com/securego/gosec/v2/rules/implicit_aliasing.go +++ b/vendor/github.com/securego/gosec/v2/rules/implicit_aliasing.go @@ -5,10 +5,11 @@ import ( "go/token" "github.com/securego/gosec/v2" + "github.com/securego/gosec/v2/issue" ) type implicitAliasing struct { - gosec.MetaData + issue.MetaData aliases map[*ast.Object]struct{} rightBrace token.Pos acceptableAlias []*ast.UnaryExpr @@ -27,7 +28,7 @@ func containsUnary(exprs []*ast.UnaryExpr, expr *ast.UnaryExpr) bool { return false } -func (r *implicitAliasing) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { +func (r *implicitAliasing) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) { switch node := n.(type) { case *ast.RangeStmt: // When presented with a range statement, get the underlying Object bound to @@ -73,7 +74,7 @@ func (r *implicitAliasing) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, er // If we find a unary op of & (reference) of an object within r.aliases, complain. if ident, ok := node.X.(*ast.Ident); ok && node.Op.String() == "&" { if _, contains := r.aliases[ident.Obj]; contains { - return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil + return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil } } case *ast.ReturnStmt: @@ -89,15 +90,15 @@ func (r *implicitAliasing) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, er } // NewImplicitAliasing detects implicit memory aliasing of type: for blah := SomeCall() {... SomeOtherCall(&blah) ...} -func NewImplicitAliasing(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { +func NewImplicitAliasing(id string, _ gosec.Config) (gosec.Rule, []ast.Node) { return &implicitAliasing{ aliases: make(map[*ast.Object]struct{}), rightBrace: token.NoPos, acceptableAlias: make([]*ast.UnaryExpr, 0), - MetaData: gosec.MetaData{ + MetaData: issue.MetaData{ ID: id, - Severity: gosec.Medium, - Confidence: gosec.Medium, + Severity: issue.Medium, + Confidence: issue.Medium, What: "Implicit memory aliasing in for loop.", }, }, []ast.Node{(*ast.RangeStmt)(nil), (*ast.UnaryExpr)(nil), (*ast.ReturnStmt)(nil)} diff --git a/vendor/github.com/securego/gosec/v2/rules/integer_overflow.go b/vendor/github.com/securego/gosec/v2/rules/integer_overflow.go index f55211a9..1d579066 100644 --- a/vendor/github.com/securego/gosec/v2/rules/integer_overflow.go +++ b/vendor/github.com/securego/gosec/v2/rules/integer_overflow.go @@ -19,10 +19,11 @@ import ( "go/ast" "github.com/securego/gosec/v2" + "github.com/securego/gosec/v2/issue" ) type integerOverflowCheck struct { - gosec.MetaData + issue.MetaData calls gosec.CallList } @@ -30,7 +31,7 @@ func (i *integerOverflowCheck) ID() string { return i.MetaData.ID } -func (i *integerOverflowCheck) Match(node ast.Node, ctx *gosec.Context) (*gosec.Issue, error) { +func (i *integerOverflowCheck) Match(node ast.Node, ctx *gosec.Context) (*issue.Issue, error) { var atoiVarObj map[*ast.Object]ast.Node // To check multiple lines, ctx.PassedValues is used to store temporary data. @@ -63,7 +64,7 @@ func (i *integerOverflowCheck) Match(node ast.Node, ctx *gosec.Context) (*gosec. if idt, ok := n.Args[0].(*ast.Ident); ok { if _, ok := atoiVarObj[idt.Obj]; ok { // Detect int32(v) and int16(v) - return gosec.NewIssue(ctx, n, i.ID(), i.What, i.Severity, i.Confidence), nil + return ctx.NewIssue(n, i.ID(), i.What, i.Severity, i.Confidence), nil } } } @@ -74,14 +75,14 @@ func (i *integerOverflowCheck) Match(node ast.Node, ctx *gosec.Context) (*gosec. } // NewIntegerOverflowCheck detects if there is potential Integer OverFlow -func NewIntegerOverflowCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { +func NewIntegerOverflowCheck(id string, _ gosec.Config) (gosec.Rule, []ast.Node) { calls := gosec.NewCallList() calls.Add("strconv", "Atoi") return &integerOverflowCheck{ - MetaData: gosec.MetaData{ + MetaData: issue.MetaData{ ID: id, - Severity: gosec.High, - Confidence: gosec.Medium, + Severity: issue.High, + Confidence: issue.Medium, What: "Potential Integer overflow made by strconv.Atoi result conversion to int16/32", }, calls: calls, diff --git a/vendor/github.com/securego/gosec/v2/rules/math_big_rat.go b/vendor/github.com/securego/gosec/v2/rules/math_big_rat.go index 69037e18..1aac1fa2 100644 --- a/vendor/github.com/securego/gosec/v2/rules/math_big_rat.go +++ b/vendor/github.com/securego/gosec/v2/rules/math_big_rat.go @@ -4,10 +4,11 @@ import ( "go/ast" "github.com/securego/gosec/v2" + "github.com/securego/gosec/v2/issue" ) type usingOldMathBig struct { - gosec.MetaData + issue.MetaData calls gosec.CallList } @@ -15,18 +16,18 @@ func (r *usingOldMathBig) ID() string { return r.MetaData.ID } -func (r *usingOldMathBig) Match(node ast.Node, ctx *gosec.Context) (gi *gosec.Issue, err error) { +func (r *usingOldMathBig) Match(node ast.Node, ctx *gosec.Context) (gi *issue.Issue, err error) { if callExpr := r.calls.ContainsPkgCallExpr(node, ctx, false); callExpr == nil { return nil, nil } - confidence := gosec.Low + confidence := issue.Low major, minor, build := gosec.GoVersion() if major == 1 && (minor == 16 && build < 14 || minor == 17 && build < 7) { - confidence = gosec.Medium + confidence = issue.Medium } - return gosec.NewIssue(ctx, node, r.ID(), r.What, r.Severity, confidence), nil + return ctx.NewIssue(node, r.ID(), r.What, r.Severity, confidence), nil } // NewUsingOldMathBig rule detects the use of Rat.SetString from math/big. @@ -35,10 +36,10 @@ func NewUsingOldMathBig(id string, _ gosec.Config) (gosec.Rule, []ast.Node) { calls.Add("math/big.Rat", "SetString") return &usingOldMathBig{ calls: calls, - MetaData: gosec.MetaData{ + MetaData: issue.MetaData{ ID: id, What: "Potential uncontrolled memory consumption in Rat.SetString (CVE-2022-23772)", - Severity: gosec.High, + Severity: issue.High, }, }, []ast.Node{(*ast.CallExpr)(nil)} } diff --git a/vendor/github.com/securego/gosec/v2/rules/pprof.go b/vendor/github.com/securego/gosec/v2/rules/pprof.go index 4c99af75..68498dd5 100644 --- a/vendor/github.com/securego/gosec/v2/rules/pprof.go +++ b/vendor/github.com/securego/gosec/v2/rules/pprof.go @@ -4,10 +4,11 @@ import ( "go/ast" "github.com/securego/gosec/v2" + "github.com/securego/gosec/v2/issue" ) type pprofCheck struct { - gosec.MetaData + issue.MetaData importPath string importName string } @@ -18,22 +19,22 @@ func (p *pprofCheck) ID() string { } // Match checks for pprof imports -func (p *pprofCheck) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { +func (p *pprofCheck) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) { if node, ok := n.(*ast.ImportSpec); ok { if p.importPath == unquote(node.Path.Value) && node.Name != nil && p.importName == node.Name.Name { - return gosec.NewIssue(c, node, p.ID(), p.What, p.Severity, p.Confidence), nil + return c.NewIssue(node, p.ID(), p.What, p.Severity, p.Confidence), nil } } return nil, nil } // NewPprofCheck detects when the profiling endpoint is automatically exposed -func NewPprofCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { +func NewPprofCheck(id string, _ gosec.Config) (gosec.Rule, []ast.Node) { return &pprofCheck{ - MetaData: gosec.MetaData{ + MetaData: issue.MetaData{ ID: id, - Severity: gosec.High, - Confidence: gosec.High, + Severity: issue.High, + Confidence: issue.High, What: "Profiling endpoint is automatically exposed on /debug/pprof", }, importPath: "net/http/pprof", diff --git a/vendor/github.com/securego/gosec/v2/rules/rand.go b/vendor/github.com/securego/gosec/v2/rules/rand.go index 055adce4..4491fd92 100644 --- a/vendor/github.com/securego/gosec/v2/rules/rand.go +++ b/vendor/github.com/securego/gosec/v2/rules/rand.go @@ -18,10 +18,11 @@ import ( "go/ast" "github.com/securego/gosec/v2" + "github.com/securego/gosec/v2/issue" ) type weakRand struct { - gosec.MetaData + issue.MetaData funcNames []string packagePath string } @@ -30,10 +31,10 @@ func (w *weakRand) ID() string { return w.MetaData.ID } -func (w *weakRand) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { +func (w *weakRand) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) { for _, funcName := range w.funcNames { if _, matched := gosec.MatchCallByPackage(n, c, w.packagePath, funcName); matched { - return gosec.NewIssue(c, n, w.ID(), w.What, w.Severity, w.Confidence), nil + return c.NewIssue(n, w.ID(), w.What, w.Severity, w.Confidence), nil } } @@ -41,17 +42,17 @@ func (w *weakRand) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { } // NewWeakRandCheck detects the use of random number generator that isn't cryptographically secure -func NewWeakRandCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { +func NewWeakRandCheck(id string, _ gosec.Config) (gosec.Rule, []ast.Node) { return &weakRand{ funcNames: []string{ "New", "Read", "Float32", "Float64", "Int", "Int31", "Int31n", "Int63", "Int63n", "Intn", "NormalFloat64", "Uint32", "Uint64", }, packagePath: "math/rand", - MetaData: gosec.MetaData{ + MetaData: issue.MetaData{ ID: id, - Severity: gosec.High, - Confidence: gosec.Medium, + Severity: issue.High, + Confidence: issue.Medium, What: "Use of weak random number generator (math/rand instead of crypto/rand)", }, }, []ast.Node{(*ast.CallExpr)(nil)} diff --git a/vendor/github.com/securego/gosec/v2/rules/readfile.go b/vendor/github.com/securego/gosec/v2/rules/readfile.go index 8dcf0532..7ef4bbad 100644 --- a/vendor/github.com/securego/gosec/v2/rules/readfile.go +++ b/vendor/github.com/securego/gosec/v2/rules/readfile.go @@ -19,10 +19,11 @@ import ( "go/types" "github.com/securego/gosec/v2" + "github.com/securego/gosec/v2/issue" ) type readfile struct { - gosec.MetaData + issue.MetaData gosec.CallList pathJoin gosec.CallList clean gosec.CallList @@ -80,13 +81,17 @@ func (r *readfile) isFilepathClean(n *ast.Ident, c *gosec.Context) bool { func (r *readfile) trackFilepathClean(n ast.Node) { if clean, ok := n.(*ast.CallExpr); ok && len(clean.Args) > 0 { if ident, ok := clean.Args[0].(*ast.Ident); ok { - r.cleanedVar[ident.Obj.Decl] = n + // ident.Obj may be nil if the referenced declaration is in another file. It also may be incorrect. + // if it is nil, do not follow it. + if ident.Obj != nil { + r.cleanedVar[ident.Obj.Decl] = n + } } } } // Match inspects AST nodes to determine if the match the methods `os.Open` or `ioutil.ReadFile` -func (r *readfile) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { +func (r *readfile) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) { if node := r.clean.ContainsPkgCallExpr(n, c, false); node != nil { r.trackFilepathClean(n) return nil, nil @@ -96,14 +101,14 @@ func (r *readfile) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { // eg. os.Open(filepath.Join("/tmp/", file)) if callExpr, ok := arg.(*ast.CallExpr); ok { if r.isJoinFunc(callExpr, c) { - return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil + return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil } } // handles binary string concatenation eg. ioutil.Readfile("/tmp/" + file + "/blob") if binExp, ok := arg.(*ast.BinaryExpr); ok { // resolve all found identities from the BinaryExpr if _, ok := gosec.FindVarIdentities(binExp, c); ok { - return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil + return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil } } @@ -112,7 +117,7 @@ func (r *readfile) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { if _, ok := obj.(*types.Var); ok && !gosec.TryResolve(ident, c) && !r.isFilepathClean(ident, c) { - return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil + return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil } } } @@ -121,16 +126,16 @@ func (r *readfile) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { } // NewReadFile detects cases where we read files -func NewReadFile(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { +func NewReadFile(id string, _ gosec.Config) (gosec.Rule, []ast.Node) { rule := &readfile{ pathJoin: gosec.NewCallList(), clean: gosec.NewCallList(), CallList: gosec.NewCallList(), - MetaData: gosec.MetaData{ + MetaData: issue.MetaData{ ID: id, What: "Potential file inclusion via variable", - Severity: gosec.Medium, - Confidence: gosec.High, + Severity: issue.Medium, + Confidence: issue.High, }, cleanedVar: map[any]ast.Node{}, } diff --git a/vendor/github.com/securego/gosec/v2/rules/rsa.go b/vendor/github.com/securego/gosec/v2/rules/rsa.go index f2ed5db5..331e7fc8 100644 --- a/vendor/github.com/securego/gosec/v2/rules/rsa.go +++ b/vendor/github.com/securego/gosec/v2/rules/rsa.go @@ -19,10 +19,11 @@ import ( "go/ast" "github.com/securego/gosec/v2" + "github.com/securego/gosec/v2/issue" ) type weakKeyStrength struct { - gosec.MetaData + issue.MetaData calls gosec.CallList bits int } @@ -31,27 +32,27 @@ func (w *weakKeyStrength) ID() string { return w.MetaData.ID } -func (w *weakKeyStrength) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { +func (w *weakKeyStrength) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) { if callExpr := w.calls.ContainsPkgCallExpr(n, c, false); callExpr != nil { if bits, err := gosec.GetInt(callExpr.Args[1]); err == nil && bits < (int64)(w.bits) { - return gosec.NewIssue(c, n, w.ID(), w.What, w.Severity, w.Confidence), nil + return c.NewIssue(n, w.ID(), w.What, w.Severity, w.Confidence), nil } } return nil, nil } // NewWeakKeyStrength builds a rule that detects RSA keys < 2048 bits -func NewWeakKeyStrength(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { +func NewWeakKeyStrength(id string, _ gosec.Config) (gosec.Rule, []ast.Node) { calls := gosec.NewCallList() calls.Add("crypto/rsa", "GenerateKey") bits := 2048 return &weakKeyStrength{ calls: calls, bits: bits, - MetaData: gosec.MetaData{ + MetaData: issue.MetaData{ ID: id, - Severity: gosec.Medium, - Confidence: gosec.High, + Severity: issue.Medium, + Confidence: issue.High, What: fmt.Sprintf("RSA keys should be at least %d bits", bits), }, }, []ast.Node{(*ast.CallExpr)(nil)} diff --git a/vendor/github.com/securego/gosec/v2/rules/rulelist.go b/vendor/github.com/securego/gosec/v2/rules/rulelist.go index b97813ed..d856ecca 100644 --- a/vendor/github.com/securego/gosec/v2/rules/rulelist.go +++ b/vendor/github.com/securego/gosec/v2/rules/rulelist.go @@ -91,7 +91,6 @@ func Generate(trackSuppressions bool, filters ...RuleFilter) RuleList { {"G304", "File path provided as taint input", NewReadFile}, {"G305", "File path traversal when extracting zip archive", NewArchive}, {"G306", "Poor file permissions used when writing to a file", NewWritePerms}, - {"G307", "Unsafe defer call of a method returning an error", NewDeferredClosing}, // crypto {"G401", "Detect the usage of DES, RC4, MD5 or SHA1", NewUsesWeakCryptography}, diff --git a/vendor/github.com/securego/gosec/v2/rules/slowloris.go b/vendor/github.com/securego/gosec/v2/rules/slowloris.go index 60b5e952..70db73f5 100644 --- a/vendor/github.com/securego/gosec/v2/rules/slowloris.go +++ b/vendor/github.com/securego/gosec/v2/rules/slowloris.go @@ -18,10 +18,11 @@ import ( "go/ast" "github.com/securego/gosec/v2" + "github.com/securego/gosec/v2/issue" ) type slowloris struct { - gosec.MetaData + issue.MetaData } func (r *slowloris) ID() string { @@ -44,13 +45,13 @@ func containsReadHeaderTimeout(node *ast.CompositeLit) bool { return false } -func (r *slowloris) Match(n ast.Node, ctx *gosec.Context) (*gosec.Issue, error) { +func (r *slowloris) Match(n ast.Node, ctx *gosec.Context) (*issue.Issue, error) { switch node := n.(type) { case *ast.CompositeLit: actualType := ctx.Info.TypeOf(node.Type) if actualType != nil && actualType.String() == "net/http.Server" { if !containsReadHeaderTimeout(node) { - return gosec.NewIssue(ctx, node, r.ID(), r.What, r.Severity, r.Confidence), nil + return ctx.NewIssue(node, r.ID(), r.What, r.Severity, r.Confidence), nil } } } @@ -58,13 +59,13 @@ func (r *slowloris) Match(n ast.Node, ctx *gosec.Context) (*gosec.Issue, error) } // NewSlowloris attempts to find the http.Server struct and check if the ReadHeaderTimeout is configured. -func NewSlowloris(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { +func NewSlowloris(id string, _ gosec.Config) (gosec.Rule, []ast.Node) { return &slowloris{ - MetaData: gosec.MetaData{ + MetaData: issue.MetaData{ ID: id, What: "Potential Slowloris Attack because ReadHeaderTimeout is not configured in the http.Server", - Confidence: gosec.Low, - Severity: gosec.Medium, + Confidence: issue.Low, + Severity: issue.Medium, }, }, []ast.Node{(*ast.CompositeLit)(nil)} } diff --git a/vendor/github.com/securego/gosec/v2/rules/sql.go b/vendor/github.com/securego/gosec/v2/rules/sql.go index ee99737d..4085b5d2 100644 --- a/vendor/github.com/securego/gosec/v2/rules/sql.go +++ b/vendor/github.com/securego/gosec/v2/rules/sql.go @@ -20,10 +20,11 @@ import ( "regexp" "github.com/securego/gosec/v2" + "github.com/securego/gosec/v2/issue" ) type sqlStatement struct { - gosec.MetaData + issue.MetaData gosec.CallList // Contains a list of patterns which must all match for the rule to match. @@ -113,7 +114,7 @@ func (s *sqlStrConcat) checkObject(n *ast.Ident, c *gosec.Context) bool { } // checkQuery verifies if the query parameters is a string concatenation -func (s *sqlStrConcat) checkQuery(call *ast.CallExpr, ctx *gosec.Context) (*gosec.Issue, error) { +func (s *sqlStrConcat) checkQuery(call *ast.CallExpr, ctx *gosec.Context) (*issue.Issue, error) { query, err := findQueryArg(call, ctx) if err != nil { return nil, err @@ -134,7 +135,7 @@ func (s *sqlStrConcat) checkQuery(call *ast.CallExpr, ctx *gosec.Context) (*gose if op, ok := op.(*ast.Ident); ok && s.checkObject(op, ctx) { continue } - return gosec.NewIssue(ctx, be, s.ID(), s.What, s.Severity, s.Confidence), nil + return ctx.NewIssue(be, s.ID(), s.What, s.Severity, s.Confidence), nil } } } @@ -143,7 +144,7 @@ func (s *sqlStrConcat) checkQuery(call *ast.CallExpr, ctx *gosec.Context) (*gose } // Checks SQL query concatenation issues such as "SELECT * FROM table WHERE " + " ' OR 1=1" -func (s *sqlStrConcat) Match(n ast.Node, ctx *gosec.Context) (*gosec.Issue, error) { +func (s *sqlStrConcat) Match(n ast.Node, ctx *gosec.Context) (*issue.Issue, error) { switch stmt := n.(type) { case *ast.AssignStmt: for _, expr := range stmt.Rhs { @@ -160,16 +161,16 @@ func (s *sqlStrConcat) Match(n ast.Node, ctx *gosec.Context) (*gosec.Issue, erro } // NewSQLStrConcat looks for cases where we are building SQL strings via concatenation -func NewSQLStrConcat(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { +func NewSQLStrConcat(id string, _ gosec.Config) (gosec.Rule, []ast.Node) { rule := &sqlStrConcat{ sqlStatement: sqlStatement{ patterns: []*regexp.Regexp{ regexp.MustCompile(`(?i)(SELECT|DELETE|INSERT|UPDATE|INTO|FROM|WHERE) `), }, - MetaData: gosec.MetaData{ + MetaData: issue.MetaData{ ID: id, - Severity: gosec.Medium, - Confidence: gosec.High, + Severity: issue.Medium, + Confidence: issue.High, What: "SQL string concatenation", }, CallList: gosec.NewCallList(), @@ -212,7 +213,7 @@ func (s *sqlStrFormat) constObject(e ast.Expr, c *gosec.Context) bool { return false } -func (s *sqlStrFormat) checkQuery(call *ast.CallExpr, ctx *gosec.Context) (*gosec.Issue, error) { +func (s *sqlStrFormat) checkQuery(call *ast.CallExpr, ctx *gosec.Context) (*issue.Issue, error) { query, err := findQueryArg(call, ctx) if err != nil { return nil, err @@ -233,7 +234,7 @@ func (s *sqlStrFormat) checkQuery(call *ast.CallExpr, ctx *gosec.Context) (*gose return nil, nil } -func (s *sqlStrFormat) checkFormatting(n ast.Node, ctx *gosec.Context) *gosec.Issue { +func (s *sqlStrFormat) checkFormatting(n ast.Node, ctx *gosec.Context) *issue.Issue { // argIndex changes the function argument which gets matched to the regex argIndex := 0 if node := s.fmtCalls.ContainsPkgCallExpr(n, ctx, false); node != nil { @@ -286,14 +287,14 @@ func (s *sqlStrFormat) checkFormatting(n ast.Node, ctx *gosec.Context) *gosec.Is } } if s.MatchPatterns(formatter) { - return gosec.NewIssue(ctx, n, s.ID(), s.What, s.Severity, s.Confidence) + return ctx.NewIssue(n, s.ID(), s.What, s.Severity, s.Confidence) } } return nil } // Check SQL query formatting issues such as "fmt.Sprintf("SELECT * FROM foo where '%s', userInput)" -func (s *sqlStrFormat) Match(n ast.Node, ctx *gosec.Context) (*gosec.Issue, error) { +func (s *sqlStrFormat) Match(n ast.Node, ctx *gosec.Context) (*issue.Issue, error) { switch stmt := n.(type) { case *ast.AssignStmt: for _, expr := range stmt.Rhs { @@ -323,7 +324,7 @@ func (s *sqlStrFormat) Match(n ast.Node, ctx *gosec.Context) (*gosec.Issue, erro } // NewSQLStrFormat looks for cases where we're building SQL query strings using format strings -func NewSQLStrFormat(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { +func NewSQLStrFormat(id string, _ gosec.Config) (gosec.Rule, []ast.Node) { rule := &sqlStrFormat{ CallList: gosec.NewCallList(), fmtCalls: gosec.NewCallList(), @@ -334,10 +335,10 @@ func NewSQLStrFormat(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { regexp.MustCompile("(?i)(SELECT|DELETE|INSERT|UPDATE|INTO|FROM|WHERE)( |\n|\r|\t)"), regexp.MustCompile("%[^bdoxXfFp]"), }, - MetaData: gosec.MetaData{ + MetaData: issue.MetaData{ ID: id, - Severity: gosec.Medium, - Confidence: gosec.High, + Severity: issue.Medium, + Confidence: issue.High, What: "SQL string formatting", }, }, diff --git a/vendor/github.com/securego/gosec/v2/rules/ssh.go b/vendor/github.com/securego/gosec/v2/rules/ssh.go index 01f37da5..e2ba5a3f 100644 --- a/vendor/github.com/securego/gosec/v2/rules/ssh.go +++ b/vendor/github.com/securego/gosec/v2/rules/ssh.go @@ -4,10 +4,11 @@ import ( "go/ast" "github.com/securego/gosec/v2" + "github.com/securego/gosec/v2/issue" ) type sshHostKey struct { - gosec.MetaData + issue.MetaData pkg string calls []string } @@ -16,23 +17,23 @@ func (r *sshHostKey) ID() string { return r.MetaData.ID } -func (r *sshHostKey) Match(n ast.Node, c *gosec.Context) (gi *gosec.Issue, err error) { +func (r *sshHostKey) Match(n ast.Node, c *gosec.Context) (gi *issue.Issue, err error) { if _, matches := gosec.MatchCallByPackage(n, c, r.pkg, r.calls...); matches { - return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil + return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil } return nil, nil } // NewSSHHostKey rule detects the use of insecure ssh HostKeyCallback. -func NewSSHHostKey(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { +func NewSSHHostKey(id string, _ gosec.Config) (gosec.Rule, []ast.Node) { return &sshHostKey{ pkg: "golang.org/x/crypto/ssh", calls: []string{"InsecureIgnoreHostKey"}, - MetaData: gosec.MetaData{ + MetaData: issue.MetaData{ ID: id, What: "Use of ssh InsecureIgnoreHostKey should be audited", - Severity: gosec.Medium, - Confidence: gosec.High, + Severity: issue.Medium, + Confidence: issue.High, }, }, []ast.Node{(*ast.CallExpr)(nil)} } diff --git a/vendor/github.com/securego/gosec/v2/rules/ssrf.go b/vendor/github.com/securego/gosec/v2/rules/ssrf.go index 86bb8278..dbf01081 100644 --- a/vendor/github.com/securego/gosec/v2/rules/ssrf.go +++ b/vendor/github.com/securego/gosec/v2/rules/ssrf.go @@ -5,10 +5,11 @@ import ( "go/types" "github.com/securego/gosec/v2" + "github.com/securego/gosec/v2/issue" ) type ssrf struct { - gosec.MetaData + issue.MetaData gosec.CallList } @@ -40,25 +41,25 @@ func (r *ssrf) ResolveVar(n *ast.CallExpr, c *gosec.Context) bool { } // Match inspects AST nodes to determine if certain net/http methods are called with variable input -func (r *ssrf) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { +func (r *ssrf) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) { // Call expression is using http package directly if node := r.ContainsPkgCallExpr(n, c, false); node != nil { if r.ResolveVar(node, c) { - return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil + return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil } } return nil, nil } // NewSSRFCheck detects cases where HTTP requests are sent -func NewSSRFCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { +func NewSSRFCheck(id string, _ gosec.Config) (gosec.Rule, []ast.Node) { rule := &ssrf{ CallList: gosec.NewCallList(), - MetaData: gosec.MetaData{ + MetaData: issue.MetaData{ ID: id, What: "Potential HTTP request made with variable url", - Severity: gosec.Medium, - Confidence: gosec.Medium, + Severity: issue.Medium, + Confidence: issue.Medium, }, } rule.AddAll("net/http", "Do", "Get", "Head", "Post", "PostForm", "RoundTrip") diff --git a/vendor/github.com/securego/gosec/v2/rules/subproc.go b/vendor/github.com/securego/gosec/v2/rules/subproc.go index 2b6cb186..ea50d692 100644 --- a/vendor/github.com/securego/gosec/v2/rules/subproc.go +++ b/vendor/github.com/securego/gosec/v2/rules/subproc.go @@ -19,10 +19,11 @@ import ( "go/types" "github.com/securego/gosec/v2" + "github.com/securego/gosec/v2/issue" ) type subprocess struct { - gosec.MetaData + issue.MetaData gosec.CallList } @@ -39,7 +40,7 @@ func (r *subprocess) ID() string { // is unsafe. For example: // // syscall.Exec("echo", "foobar" + tainted) -func (r *subprocess) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { +func (r *subprocess) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) { if node := r.ContainsPkgCallExpr(n, c, false); node != nil { args := node.Args if r.isContext(n, c) { @@ -64,7 +65,7 @@ func (r *subprocess) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { _, assignment := ident.Obj.Decl.(*ast.AssignStmt) if variable && assignment { if !gosec.TryResolve(ident, c) { - return gosec.NewIssue(c, n, r.ID(), "Subprocess launched with variable", gosec.Medium, gosec.High), nil + return c.NewIssue(n, r.ID(), "Subprocess launched with variable", issue.Medium, issue.High), nil } } case *ast.Field: @@ -74,21 +75,21 @@ func (r *subprocess) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { vv, vvok := obj.(*types.Var) if vvok && vv.Parent().Lookup(ident.Name) == nil { - return gosec.NewIssue(c, n, r.ID(), "Subprocess launched with variable", gosec.Medium, gosec.High), nil + return c.NewIssue(n, r.ID(), "Subprocess launched with variable", issue.Medium, issue.High), nil } } case *ast.ValueSpec: _, valueSpec := ident.Obj.Decl.(*ast.ValueSpec) if variable && valueSpec { if !gosec.TryResolve(ident, c) { - return gosec.NewIssue(c, n, r.ID(), "Subprocess launched with variable", gosec.Medium, gosec.High), nil + return c.NewIssue(n, r.ID(), "Subprocess launched with variable", issue.Medium, issue.High), nil } } } } } else if !gosec.TryResolve(arg, c) { // the arg is not a constant or a variable but instead a function call or os.Args[i] - return gosec.NewIssue(c, n, r.ID(), "Subprocess launched with a potential tainted input or cmd arguments", gosec.Medium, gosec.High), nil + return c.NewIssue(n, r.ID(), "Subprocess launched with a potential tainted input or cmd arguments", issue.Medium, issue.High), nil } } } @@ -109,8 +110,8 @@ func (r *subprocess) isContext(n ast.Node, ctx *gosec.Context) bool { } // NewSubproc detects cases where we are forking out to an external process -func NewSubproc(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { - rule := &subprocess{gosec.MetaData{ID: id}, gosec.NewCallList()} +func NewSubproc(id string, _ gosec.Config) (gosec.Rule, []ast.Node) { + rule := &subprocess{issue.MetaData{ID: id}, gosec.NewCallList()} rule.Add("os/exec", "Command") rule.Add("os/exec", "CommandContext") rule.Add("syscall", "Exec") diff --git a/vendor/github.com/securego/gosec/v2/rules/tempfiles.go b/vendor/github.com/securego/gosec/v2/rules/tempfiles.go index 63822c09..6fef52a2 100644 --- a/vendor/github.com/securego/gosec/v2/rules/tempfiles.go +++ b/vendor/github.com/securego/gosec/v2/rules/tempfiles.go @@ -19,10 +19,11 @@ import ( "regexp" "github.com/securego/gosec/v2" + "github.com/securego/gosec/v2/issue" ) type badTempFile struct { - gosec.MetaData + issue.MetaData calls gosec.CallList args *regexp.Regexp argCalls gosec.CallList @@ -33,15 +34,15 @@ func (t *badTempFile) ID() string { return t.MetaData.ID } -func (t *badTempFile) findTempDirArgs(n ast.Node, c *gosec.Context, suspect ast.Node) *gosec.Issue { +func (t *badTempFile) findTempDirArgs(n ast.Node, c *gosec.Context, suspect ast.Node) *issue.Issue { if s, e := gosec.GetString(suspect); e == nil { if t.args.MatchString(s) { - return gosec.NewIssue(c, n, t.ID(), t.What, t.Severity, t.Confidence) + return c.NewIssue(n, t.ID(), t.What, t.Severity, t.Confidence) } return nil } if ce := t.argCalls.ContainsPkgCallExpr(suspect, c, false); ce != nil { - return gosec.NewIssue(c, n, t.ID(), t.What, t.Severity, t.Confidence) + return c.NewIssue(n, t.ID(), t.What, t.Severity, t.Confidence) } if be, ok := suspect.(*ast.BinaryExpr); ok { if ops := gosec.GetBinaryExprOperands(be); len(ops) != 0 { @@ -55,7 +56,7 @@ func (t *badTempFile) findTempDirArgs(n ast.Node, c *gosec.Context, suspect ast. return nil } -func (t *badTempFile) Match(n ast.Node, c *gosec.Context) (gi *gosec.Issue, err error) { +func (t *badTempFile) Match(n ast.Node, c *gosec.Context) (gi *issue.Issue, err error) { if node := t.calls.ContainsPkgCallExpr(n, c, false); node != nil { return t.findTempDirArgs(n, c, node.Args[0]), nil } @@ -63,7 +64,7 @@ func (t *badTempFile) Match(n ast.Node, c *gosec.Context) (gi *gosec.Issue, err } // NewBadTempFile detects direct writes to predictable path in temporary directory -func NewBadTempFile(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { +func NewBadTempFile(id string, _ gosec.Config) (gosec.Rule, []ast.Node) { calls := gosec.NewCallList() calls.Add("io/ioutil", "WriteFile") calls.AddAll("os", "Create", "WriteFile") @@ -77,10 +78,10 @@ func NewBadTempFile(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { args: regexp.MustCompile(`^(/(usr|var))?/tmp(/.*)?$`), argCalls: argCalls, nestedCalls: nestedCalls, - MetaData: gosec.MetaData{ + MetaData: issue.MetaData{ ID: id, - Severity: gosec.Medium, - Confidence: gosec.High, + Severity: issue.Medium, + Confidence: issue.High, What: "File creation in shared tmp directory without using ioutil.Tempfile", }, }, []ast.Node{(*ast.CallExpr)(nil)} diff --git a/vendor/github.com/securego/gosec/v2/rules/templates.go b/vendor/github.com/securego/gosec/v2/rules/templates.go index 1eec7fba..728766f4 100644 --- a/vendor/github.com/securego/gosec/v2/rules/templates.go +++ b/vendor/github.com/securego/gosec/v2/rules/templates.go @@ -18,10 +18,11 @@ import ( "go/ast" "github.com/securego/gosec/v2" + "github.com/securego/gosec/v2/issue" ) type templateCheck struct { - gosec.MetaData + issue.MetaData calls gosec.CallList } @@ -29,11 +30,11 @@ func (t *templateCheck) ID() string { return t.MetaData.ID } -func (t *templateCheck) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { +func (t *templateCheck) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) { if node := t.calls.ContainsPkgCallExpr(n, c, false); node != nil { for _, arg := range node.Args { if _, ok := arg.(*ast.BasicLit); !ok { // basic lits are safe - return gosec.NewIssue(c, n, t.ID(), t.What, t.Severity, t.Confidence), nil + return c.NewIssue(n, t.ID(), t.What, t.Severity, t.Confidence), nil } } } @@ -42,7 +43,7 @@ func (t *templateCheck) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error // NewTemplateCheck constructs the template check rule. This rule is used to // find use of templates where HTML/JS escaping is not being used -func NewTemplateCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { +func NewTemplateCheck(id string, _ gosec.Config) (gosec.Rule, []ast.Node) { calls := gosec.NewCallList() calls.Add("html/template", "HTML") calls.Add("html/template", "HTMLAttr") @@ -50,10 +51,10 @@ func NewTemplateCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { calls.Add("html/template", "URL") return &templateCheck{ calls: calls, - MetaData: gosec.MetaData{ + MetaData: issue.MetaData{ ID: id, - Severity: gosec.Medium, - Confidence: gosec.Low, + Severity: issue.Medium, + Confidence: issue.Low, What: "The used method does not auto-escape HTML. This can potentially lead to 'Cross-site Scripting' vulnerabilities, in case the attacker controls the input.", }, }, []ast.Node{(*ast.CallExpr)(nil)} diff --git a/vendor/github.com/securego/gosec/v2/rules/tls.go b/vendor/github.com/securego/gosec/v2/rules/tls.go index 1cc3a298..65a0b5a3 100644 --- a/vendor/github.com/securego/gosec/v2/rules/tls.go +++ b/vendor/github.com/securego/gosec/v2/rules/tls.go @@ -24,10 +24,11 @@ import ( "strconv" "github.com/securego/gosec/v2" + "github.com/securego/gosec/v2/issue" ) type insecureConfigTLS struct { - gosec.MetaData + issue.MetaData MinVersion int64 MaxVersion int64 requiredType string @@ -49,13 +50,13 @@ func stringInSlice(a string, list []string) bool { return false } -func (t *insecureConfigTLS) processTLSCipherSuites(n ast.Node, c *gosec.Context) *gosec.Issue { +func (t *insecureConfigTLS) processTLSCipherSuites(n ast.Node, c *gosec.Context) *issue.Issue { if ciphers, ok := n.(*ast.CompositeLit); ok { for _, cipher := range ciphers.Elts { if ident, ok := cipher.(*ast.SelectorExpr); ok { if !stringInSlice(ident.Sel.Name, t.goodCiphers) { err := fmt.Sprintf("TLS Bad Cipher Suite: %s", ident.Sel.Name) - return gosec.NewIssue(c, ident, t.ID(), err, gosec.High, gosec.High) + return c.NewIssue(ident, t.ID(), err, issue.High, issue.High) } } } @@ -63,7 +64,7 @@ func (t *insecureConfigTLS) processTLSCipherSuites(n ast.Node, c *gosec.Context) return nil } -func (t *insecureConfigTLS) processTLSConf(n ast.Node, c *gosec.Context) *gosec.Issue { +func (t *insecureConfigTLS) processTLSConf(n ast.Node, c *gosec.Context) *issue.Issue { if kve, ok := n.(*ast.KeyValueExpr); ok { issue := t.processTLSConfVal(kve.Key, kve.Value, c) if issue != nil { @@ -83,27 +84,27 @@ func (t *insecureConfigTLS) processTLSConf(n ast.Node, c *gosec.Context) *gosec. return nil } -func (t *insecureConfigTLS) processTLSConfVal(key ast.Expr, value ast.Expr, c *gosec.Context) *gosec.Issue { +func (t *insecureConfigTLS) processTLSConfVal(key ast.Expr, value ast.Expr, c *gosec.Context) *issue.Issue { if ident, ok := key.(*ast.Ident); ok { switch ident.Name { case "InsecureSkipVerify": if node, ok := value.(*ast.Ident); ok { if node.Name != "false" { - return gosec.NewIssue(c, value, t.ID(), "TLS InsecureSkipVerify set true.", gosec.High, gosec.High) + return c.NewIssue(value, t.ID(), "TLS InsecureSkipVerify set true.", issue.High, issue.High) } } else { // TODO(tk): symbol tab look up to get the actual value - return gosec.NewIssue(c, value, t.ID(), "TLS InsecureSkipVerify may be true.", gosec.High, gosec.Low) + return c.NewIssue(value, t.ID(), "TLS InsecureSkipVerify may be true.", issue.High, issue.Low) } case "PreferServerCipherSuites": if node, ok := value.(*ast.Ident); ok { if node.Name == "false" { - return gosec.NewIssue(c, value, t.ID(), "TLS PreferServerCipherSuites set false.", gosec.Medium, gosec.High) + return c.NewIssue(value, t.ID(), "TLS PreferServerCipherSuites set false.", issue.Medium, issue.High) } } else { // TODO(tk): symbol tab look up to get the actual value - return gosec.NewIssue(c, value, t.ID(), "TLS PreferServerCipherSuites may be false.", gosec.Medium, gosec.Low) + return c.NewIssue(value, t.ID(), "TLS PreferServerCipherSuites may be false.", issue.Medium, issue.Low) } case "MinVersion": @@ -188,16 +189,16 @@ func (t *insecureConfigTLS) mapVersion(version string) int64 { return v } -func (t *insecureConfigTLS) checkVersion(n ast.Node, c *gosec.Context) *gosec.Issue { +func (t *insecureConfigTLS) checkVersion(n ast.Node, c *gosec.Context) *issue.Issue { if t.actualMaxVersion == 0 && t.actualMinVersion >= t.MinVersion { // no warning is generated since the min version is greater than the secure min version return nil } if t.actualMinVersion < t.MinVersion { - return gosec.NewIssue(c, n, t.ID(), "TLS MinVersion too low.", gosec.High, gosec.High) + return c.NewIssue(n, t.ID(), "TLS MinVersion too low.", issue.High, issue.High) } if t.actualMaxVersion < t.MaxVersion { - return gosec.NewIssue(c, n, t.ID(), "TLS MaxVersion too low.", gosec.High, gosec.High) + return c.NewIssue(n, t.ID(), "TLS MaxVersion too low.", issue.High, issue.High) } return nil } @@ -207,7 +208,7 @@ func (t *insecureConfigTLS) resetVersion() { t.actualMinVersion = 0 } -func (t *insecureConfigTLS) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { +func (t *insecureConfigTLS) Match(n ast.Node, c *gosec.Context) (*issue.Issue, error) { if complit, ok := n.(*ast.CompositeLit); ok && complit.Type != nil { actualType := c.Info.TypeOf(complit.Type) if actualType != nil && actualType.String() == t.requiredType { diff --git a/vendor/github.com/securego/gosec/v2/rules/tls_config.go b/vendor/github.com/securego/gosec/v2/rules/tls_config.go index 9bb17c24..cbbdf798 100644 --- a/vendor/github.com/securego/gosec/v2/rules/tls_config.go +++ b/vendor/github.com/securego/gosec/v2/rules/tls_config.go @@ -4,13 +4,14 @@ import ( "go/ast" "github.com/securego/gosec/v2" + "github.com/securego/gosec/v2/issue" ) // NewModernTLSCheck creates a check for Modern TLS ciphers // DO NOT EDIT - generated by tlsconfig tool -func NewModernTLSCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { +func NewModernTLSCheck(id string, _ gosec.Config) (gosec.Rule, []ast.Node) { return &insecureConfigTLS{ - MetaData: gosec.MetaData{ID: id}, + MetaData: issue.MetaData{ID: id}, requiredType: "crypto/tls.Config", MinVersion: 0x0304, MaxVersion: 0x0304, @@ -24,9 +25,9 @@ func NewModernTLSCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { // NewIntermediateTLSCheck creates a check for Intermediate TLS ciphers // DO NOT EDIT - generated by tlsconfig tool -func NewIntermediateTLSCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { +func NewIntermediateTLSCheck(id string, _ gosec.Config) (gosec.Rule, []ast.Node) { return &insecureConfigTLS{ - MetaData: gosec.MetaData{ID: id}, + MetaData: issue.MetaData{ID: id}, requiredType: "crypto/tls.Config", MinVersion: 0x0303, MaxVersion: 0x0304, @@ -50,9 +51,9 @@ func NewIntermediateTLSCheck(id string, conf gosec.Config) (gosec.Rule, []ast.No // NewOldTLSCheck creates a check for Old TLS ciphers // DO NOT EDIT - generated by tlsconfig tool -func NewOldTLSCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { +func NewOldTLSCheck(id string, _ gosec.Config) (gosec.Rule, []ast.Node) { return &insecureConfigTLS{ - MetaData: gosec.MetaData{ID: id}, + MetaData: issue.MetaData{ID: id}, requiredType: "crypto/tls.Config", MinVersion: 0x0301, MaxVersion: 0x0304, diff --git a/vendor/github.com/securego/gosec/v2/rules/unsafe.go b/vendor/github.com/securego/gosec/v2/rules/unsafe.go index 88a298fb..e1e8d023 100644 --- a/vendor/github.com/securego/gosec/v2/rules/unsafe.go +++ b/vendor/github.com/securego/gosec/v2/rules/unsafe.go @@ -18,10 +18,11 @@ import ( "go/ast" "github.com/securego/gosec/v2" + "github.com/securego/gosec/v2/issue" ) type usingUnsafe struct { - gosec.MetaData + issue.MetaData pkg string calls []string } @@ -30,24 +31,24 @@ func (r *usingUnsafe) ID() string { return r.MetaData.ID } -func (r *usingUnsafe) Match(n ast.Node, c *gosec.Context) (gi *gosec.Issue, err error) { +func (r *usingUnsafe) Match(n ast.Node, c *gosec.Context) (gi *issue.Issue, err error) { if _, matches := gosec.MatchCallByPackage(n, c, r.pkg, r.calls...); matches { - return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil + return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil } return nil, nil } // NewUsingUnsafe rule detects the use of the unsafe package. This is only // really useful for auditing purposes. -func NewUsingUnsafe(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { +func NewUsingUnsafe(id string, _ gosec.Config) (gosec.Rule, []ast.Node) { return &usingUnsafe{ pkg: "unsafe", calls: []string{"Alignof", "Offsetof", "Sizeof", "Pointer"}, - MetaData: gosec.MetaData{ + MetaData: issue.MetaData{ ID: id, What: "Use of unsafe calls should be audited", - Severity: gosec.Low, - Confidence: gosec.High, + Severity: issue.Low, + Confidence: issue.High, }, }, []ast.Node{(*ast.CallExpr)(nil)} } diff --git a/vendor/github.com/securego/gosec/v2/rules/weakcrypto.go b/vendor/github.com/securego/gosec/v2/rules/weakcrypto.go index eecb88f0..4f2ab11d 100644 --- a/vendor/github.com/securego/gosec/v2/rules/weakcrypto.go +++ b/vendor/github.com/securego/gosec/v2/rules/weakcrypto.go @@ -18,10 +18,11 @@ import ( "go/ast" "github.com/securego/gosec/v2" + "github.com/securego/gosec/v2/issue" ) type usesWeakCryptography struct { - gosec.MetaData + issue.MetaData blocklist map[string][]string } @@ -29,17 +30,17 @@ func (r *usesWeakCryptography) ID() string { return r.MetaData.ID } -func (r *usesWeakCryptography) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { +func (r *usesWeakCryptography) 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 gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil + return c.NewIssue(n, r.ID(), r.What, r.Severity, r.Confidence), nil } } return nil, nil } // NewUsesWeakCryptography detects uses of des.* md5.* or rc4.* -func NewUsesWeakCryptography(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { +func NewUsesWeakCryptography(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"} @@ -47,10 +48,10 @@ func NewUsesWeakCryptography(id string, conf gosec.Config) (gosec.Rule, []ast.No calls["crypto/rc4"] = []string{"NewCipher"} rule := &usesWeakCryptography{ blocklist: calls, - MetaData: gosec.MetaData{ + MetaData: issue.MetaData{ ID: id, - Severity: gosec.Medium, - Confidence: gosec.High, + Severity: issue.Medium, + Confidence: issue.High, What: "Use of weak cryptographic primitive", }, } diff --git a/vendor/github.com/sirupsen/logrus/README.md b/vendor/github.com/sirupsen/logrus/README.md index b042c896..d1d4a85f 100644 --- a/vendor/github.com/sirupsen/logrus/README.md +++ b/vendor/github.com/sirupsen/logrus/README.md @@ -9,7 +9,7 @@ the last thing you want from your Logging library (again...). This does not mean Logrus is dead. Logrus will continue to be maintained for security, (backwards compatible) bug fixes, and performance (where we are -limited by the interface). +limited by the interface). I believe Logrus' biggest contribution is to have played a part in today's widespread use of structured logging in Golang. There doesn't seem to be a @@ -43,7 +43,7 @@ plain text): With `log.SetFormatter(&log.JSONFormatter{})`, for easy parsing by logstash or Splunk: -```json +```text {"animal":"walrus","level":"info","msg":"A group of walrus emerges from the ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"} @@ -99,7 +99,7 @@ time="2015-03-26T01:27:38-04:00" level=fatal method=github.com/sirupsen/arcticcr ``` Note that this does add measurable overhead - the cost will depend on the version of Go, but is between 20 and 40% in recent tests with 1.6 and 1.7. You can validate this in your -environment via benchmarks: +environment via benchmarks: ``` go test -bench=.*CallerTracing ``` @@ -317,6 +317,8 @@ log.SetLevel(log.InfoLevel) It may be useful to set `log.Level = logrus.DebugLevel` in a debug or verbose environment if your application has that. +Note: If you want different log levels for global (`log.SetLevel(...)`) and syslog logging, please check the [syslog hook README](hooks/syslog/README.md#different-log-levels-for-local-and-remote-logging). + #### Entries Besides the fields added with `WithField` or `WithFields` some fields are diff --git a/vendor/github.com/sivchari/containedctx/containedctx.go b/vendor/github.com/sivchari/containedctx/containedctx.go index 5a2c2daf..0260d6a6 100644 --- a/vendor/github.com/sivchari/containedctx/containedctx.go +++ b/vendor/github.com/sivchari/containedctx/containedctx.go @@ -34,16 +34,7 @@ func run(pass *analysis.Pass) (interface{}, error) { return } for _, field := range structTyp.Fields.List { - selectorExpr, ok := field.Type.(*ast.SelectorExpr) - if !ok { - continue - } - xname, ok := selectorExpr.X.(*ast.Ident) - if !ok { - continue - } - selname := selectorExpr.Sel.Name - if xname.Name+"."+selname == "context.Context" { + if pass.TypesInfo.TypeOf(field.Type).String() == "context.Context" { pass.Reportf(field.Pos(), "found a struct that contains a context.Context field") } } diff --git a/vendor/github.com/stretchr/testify/assert/assertion_compare.go b/vendor/github.com/stretchr/testify/assert/assertion_compare.go index 95d8e59d..b774da88 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_compare.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_compare.go @@ -352,9 +352,9 @@ func compare(obj1, obj2 interface{}, kind reflect.Kind) (CompareType, bool) { // Greater asserts that the first element is greater than the second // -// assert.Greater(t, 2, 1) -// assert.Greater(t, float64(2), float64(1)) -// assert.Greater(t, "b", "a") +// assert.Greater(t, 2, 1) +// assert.Greater(t, float64(2), float64(1)) +// assert.Greater(t, "b", "a") func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -364,10 +364,10 @@ func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface // GreaterOrEqual asserts that the first element is greater than or equal to the second // -// assert.GreaterOrEqual(t, 2, 1) -// assert.GreaterOrEqual(t, 2, 2) -// assert.GreaterOrEqual(t, "b", "a") -// assert.GreaterOrEqual(t, "b", "b") +// assert.GreaterOrEqual(t, 2, 1) +// assert.GreaterOrEqual(t, 2, 2) +// assert.GreaterOrEqual(t, "b", "a") +// assert.GreaterOrEqual(t, "b", "b") func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -377,9 +377,9 @@ func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...in // Less asserts that the first element is less than the second // -// assert.Less(t, 1, 2) -// assert.Less(t, float64(1), float64(2)) -// assert.Less(t, "a", "b") +// assert.Less(t, 1, 2) +// assert.Less(t, float64(1), float64(2)) +// assert.Less(t, "a", "b") func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -389,10 +389,10 @@ func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) // LessOrEqual asserts that the first element is less than or equal to the second // -// assert.LessOrEqual(t, 1, 2) -// assert.LessOrEqual(t, 2, 2) -// assert.LessOrEqual(t, "a", "b") -// assert.LessOrEqual(t, "b", "b") +// assert.LessOrEqual(t, 1, 2) +// assert.LessOrEqual(t, 2, 2) +// assert.LessOrEqual(t, "a", "b") +// assert.LessOrEqual(t, "b", "b") func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -402,8 +402,8 @@ func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...inter // Positive asserts that the specified element is positive // -// assert.Positive(t, 1) -// assert.Positive(t, 1.23) +// assert.Positive(t, 1) +// assert.Positive(t, 1.23) func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -414,8 +414,8 @@ func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { // Negative asserts that the specified element is negative // -// assert.Negative(t, -1) -// assert.Negative(t, -1.23) +// assert.Negative(t, -1) +// assert.Negative(t, -1.23) func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/assert/assertion_format.go b/vendor/github.com/stretchr/testify/assert/assertion_format.go index 7880b8f9..84dbd6c7 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_format.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_format.go @@ -22,9 +22,9 @@ func Conditionf(t TestingT, comp Comparison, msg string, args ...interface{}) bo // Containsf asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// assert.Containsf(t, "Hello World", "World", "error message %s", "formatted") -// assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted") -// assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted") +// assert.Containsf(t, "Hello World", "World", "error message %s", "formatted") +// assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted") +// assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted") func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -56,7 +56,7 @@ func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string // Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // -// assert.Emptyf(t, obj, "error message %s", "formatted") +// assert.Emptyf(t, obj, "error message %s", "formatted") func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -66,7 +66,7 @@ func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) boo // Equalf asserts that two objects are equal. // -// assert.Equalf(t, 123, 123, "error message %s", "formatted") +// assert.Equalf(t, 123, 123, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -81,8 +81,8 @@ func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, ar // EqualErrorf asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // -// actualObj, err := SomeFunction() -// assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted") +// actualObj, err := SomeFunction() +// assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted") func EqualErrorf(t TestingT, theError error, errString string, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -90,10 +90,27 @@ func EqualErrorf(t TestingT, theError error, errString string, msg string, args return EqualError(t, theError, errString, append([]interface{}{msg}, args...)...) } +// EqualExportedValuesf asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// assert.EqualExportedValuesf(t, S{1, 2}, S{1, 3}, "error message %s", "formatted") => true +// assert.EqualExportedValuesf(t, S{1, 2}, S{2, 3}, "error message %s", "formatted") => false +func EqualExportedValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return EqualExportedValues(t, expected, actual, append([]interface{}{msg}, args...)...) +} + // EqualValuesf asserts that two objects are equal or convertable to the same types // and equal. // -// assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") +// assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -103,10 +120,10 @@ func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg stri // Errorf asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if assert.Errorf(t, err, "error message %s", "formatted") { -// assert.Equal(t, expectedErrorf, err) -// } +// actualObj, err := SomeFunction() +// if assert.Errorf(t, err, "error message %s", "formatted") { +// assert.Equal(t, expectedErrorf, err) +// } func Errorf(t TestingT, err error, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -126,8 +143,8 @@ func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...int // ErrorContainsf asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // -// actualObj, err := SomeFunction() -// assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") +// actualObj, err := SomeFunction() +// assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") func ErrorContainsf(t TestingT, theError error, contains string, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -147,7 +164,7 @@ func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface // Eventuallyf asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -155,9 +172,34 @@ func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick return Eventually(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...) } +// EventuallyWithTf asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// assert.EventuallyWithTf(t, func(c *assert.CollectT, "error message %s", "formatted") { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func EventuallyWithTf(t TestingT, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + return EventuallyWithT(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...) +} + // Exactlyf asserts that two objects are equal in value and type. // -// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted") +// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted") func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -183,7 +225,7 @@ func FailNowf(t TestingT, failureMessage string, msg string, args ...interface{} // Falsef asserts that the specified value is false. // -// assert.Falsef(t, myBool, "error message %s", "formatted") +// assert.Falsef(t, myBool, "error message %s", "formatted") func Falsef(t TestingT, value bool, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -202,9 +244,9 @@ func FileExistsf(t TestingT, path string, msg string, args ...interface{}) bool // Greaterf asserts that the first element is greater than the second // -// assert.Greaterf(t, 2, 1, "error message %s", "formatted") -// assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted") -// assert.Greaterf(t, "b", "a", "error message %s", "formatted") +// assert.Greaterf(t, 2, 1, "error message %s", "formatted") +// assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted") +// assert.Greaterf(t, "b", "a", "error message %s", "formatted") func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -214,10 +256,10 @@ func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...in // GreaterOrEqualf asserts that the first element is greater than or equal to the second // -// assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted") -// assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted") -// assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted") -// assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted") +// assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted") +// assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted") +// assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted") +// assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted") func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -228,7 +270,7 @@ func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, arg // HTTPBodyContainsf asserts that a specified handler returns a // body that contains a string. // -// assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { @@ -241,7 +283,7 @@ func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url // HTTPBodyNotContainsf asserts that a specified handler returns a // body that does not contain a string. // -// assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { @@ -253,7 +295,7 @@ func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, u // HTTPErrorf asserts that a specified handler returns an error status code. // -// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { @@ -265,7 +307,7 @@ func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, // HTTPRedirectf asserts that a specified handler returns a redirect status code. // -// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { @@ -277,7 +319,7 @@ func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url stri // HTTPStatusCodef asserts that a specified handler returns a specified status code. // -// assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") +// assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) bool { @@ -289,7 +331,7 @@ func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url st // HTTPSuccessf asserts that a specified handler returns a success status code. // -// assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") +// assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { @@ -301,7 +343,7 @@ func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url strin // Implementsf asserts that an object is implemented by the specified interface. // -// assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") +// assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -311,7 +353,7 @@ func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, ms // InDeltaf asserts that the two numerals are within delta of each other. // -// assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted") +// assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted") func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -353,9 +395,9 @@ func InEpsilonSlicef(t TestingT, expected interface{}, actual interface{}, epsil // IsDecreasingf asserts that the collection is decreasing // -// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted") -// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted") -// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted") +// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted") +// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted") +// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted") func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -365,9 +407,9 @@ func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface // IsIncreasingf asserts that the collection is increasing // -// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted") -// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted") -// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted") +// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted") +// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted") +// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted") func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -377,9 +419,9 @@ func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface // IsNonDecreasingf asserts that the collection is not decreasing // -// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted") -// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted") -// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted") +// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted") +// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted") +// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted") func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -389,9 +431,9 @@ func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interf // IsNonIncreasingf asserts that the collection is not increasing // -// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted") -// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted") -// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted") +// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted") +// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted") +// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted") func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -409,7 +451,7 @@ func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg strin // JSONEqf asserts that two JSON strings are equivalent. // -// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") +// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -420,7 +462,7 @@ func JSONEqf(t TestingT, expected string, actual string, msg string, args ...int // Lenf asserts that the specified object has specific length. // Lenf also fails if the object has a type that len() not accept. // -// assert.Lenf(t, mySlice, 3, "error message %s", "formatted") +// assert.Lenf(t, mySlice, 3, "error message %s", "formatted") func Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -430,9 +472,9 @@ func Lenf(t TestingT, object interface{}, length int, msg string, args ...interf // Lessf asserts that the first element is less than the second // -// assert.Lessf(t, 1, 2, "error message %s", "formatted") -// assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted") -// assert.Lessf(t, "a", "b", "error message %s", "formatted") +// assert.Lessf(t, 1, 2, "error message %s", "formatted") +// assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted") +// assert.Lessf(t, "a", "b", "error message %s", "formatted") func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -442,10 +484,10 @@ func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...inter // LessOrEqualf asserts that the first element is less than or equal to the second // -// assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted") -// assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted") -// assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted") -// assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted") +// assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted") +// assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted") +// assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted") +// assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted") func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -455,8 +497,8 @@ func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args . // Negativef asserts that the specified element is negative // -// assert.Negativef(t, -1, "error message %s", "formatted") -// assert.Negativef(t, -1.23, "error message %s", "formatted") +// assert.Negativef(t, -1, "error message %s", "formatted") +// assert.Negativef(t, -1.23, "error message %s", "formatted") func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -467,7 +509,7 @@ func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) bool // Neverf asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -477,7 +519,7 @@ func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time. // Nilf asserts that the specified object is nil. // -// assert.Nilf(t, err, "error message %s", "formatted") +// assert.Nilf(t, err, "error message %s", "formatted") func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -496,10 +538,10 @@ func NoDirExistsf(t TestingT, path string, msg string, args ...interface{}) bool // NoErrorf asserts that a function returned no error (i.e. `nil`). // -// actualObj, err := SomeFunction() -// if assert.NoErrorf(t, err, "error message %s", "formatted") { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// if assert.NoErrorf(t, err, "error message %s", "formatted") { +// assert.Equal(t, expectedObj, actualObj) +// } func NoErrorf(t TestingT, err error, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -519,9 +561,9 @@ func NoFileExistsf(t TestingT, path string, msg string, args ...interface{}) boo // NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted") -// assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted") -// assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted") +// assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted") +// assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted") +// assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted") func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -532,9 +574,9 @@ func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, a // NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // -// if assert.NotEmptyf(t, obj, "error message %s", "formatted") { -// assert.Equal(t, "two", obj[1]) -// } +// if assert.NotEmptyf(t, obj, "error message %s", "formatted") { +// assert.Equal(t, "two", obj[1]) +// } func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -544,7 +586,7 @@ func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) // NotEqualf asserts that the specified values are NOT equal. // -// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted") +// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -557,7 +599,7 @@ func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, // NotEqualValuesf asserts that two objects are not equal even when converted to the same type // -// assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted") +// assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted") func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -576,7 +618,7 @@ func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interf // NotNilf asserts that the specified object is not nil. // -// assert.NotNilf(t, err, "error message %s", "formatted") +// assert.NotNilf(t, err, "error message %s", "formatted") func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -586,7 +628,7 @@ func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) bo // NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. // -// assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted") +// assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted") func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -596,8 +638,8 @@ func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bo // NotRegexpf asserts that a specified regexp does not match a string. // -// assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") -// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") +// assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") +// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -607,7 +649,7 @@ func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args .. // NotSamef asserts that two pointers do not reference the same object. // -// assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted") +// assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -621,7 +663,7 @@ func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string, // NotSubsetf asserts that the specified list(array, slice...) contains not all // elements given in the specified subset(array, slice...). // -// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") +// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -639,7 +681,7 @@ func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) bool { // Panicsf asserts that the code inside the specified PanicTestFunc panics. // -// assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted") +// assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted") func Panicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -651,7 +693,7 @@ func Panicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func PanicsWithErrorf(t TestingT, errString string, f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -662,7 +704,7 @@ func PanicsWithErrorf(t TestingT, errString string, f PanicTestFunc, msg string, // PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -672,8 +714,8 @@ func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg str // Positivef asserts that the specified element is positive // -// assert.Positivef(t, 1, "error message %s", "formatted") -// assert.Positivef(t, 1.23, "error message %s", "formatted") +// assert.Positivef(t, 1, "error message %s", "formatted") +// assert.Positivef(t, 1.23, "error message %s", "formatted") func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -683,8 +725,8 @@ func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) bool // Regexpf asserts that a specified regexp matches a string. // -// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") -// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") +// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") +// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -694,7 +736,7 @@ func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...in // Samef asserts that two pointers reference the same object. // -// assert.Samef(t, ptr1, ptr2, "error message %s", "formatted") +// assert.Samef(t, ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -708,7 +750,7 @@ func Samef(t TestingT, expected interface{}, actual interface{}, msg string, arg // Subsetf asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // -// assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") +// assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -718,7 +760,7 @@ func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args // Truef asserts that the specified value is true. // -// assert.Truef(t, myBool, "error message %s", "formatted") +// assert.Truef(t, myBool, "error message %s", "formatted") func Truef(t TestingT, value bool, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -728,7 +770,7 @@ func Truef(t TestingT, value bool, msg string, args ...interface{}) bool { // WithinDurationf asserts that the two times are within duration delta of each other. // -// assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") +// assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -738,7 +780,7 @@ func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta tim // WithinRangef asserts that a time is within a time range (inclusive). // -// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/assert/assertion_forward.go b/vendor/github.com/stretchr/testify/assert/assertion_forward.go index 339515b8..b1d94aec 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_forward.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_forward.go @@ -30,9 +30,9 @@ func (a *Assertions) Conditionf(comp Comparison, msg string, args ...interface{} // Contains asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// a.Contains("Hello World", "World") -// a.Contains(["Hello", "World"], "World") -// a.Contains({"Hello": "World"}, "Hello") +// a.Contains("Hello World", "World") +// a.Contains(["Hello", "World"], "World") +// a.Contains({"Hello": "World"}, "Hello") func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -43,9 +43,9 @@ func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs .. // Containsf asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// a.Containsf("Hello World", "World", "error message %s", "formatted") -// a.Containsf(["Hello", "World"], "World", "error message %s", "formatted") -// a.Containsf({"Hello": "World"}, "Hello", "error message %s", "formatted") +// a.Containsf("Hello World", "World", "error message %s", "formatted") +// a.Containsf(["Hello", "World"], "World", "error message %s", "formatted") +// a.Containsf({"Hello": "World"}, "Hello", "error message %s", "formatted") func (a *Assertions) Containsf(s interface{}, contains interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -98,7 +98,7 @@ func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg st // Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // -// a.Empty(obj) +// a.Empty(obj) func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -109,7 +109,7 @@ func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool { // Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // -// a.Emptyf(obj, "error message %s", "formatted") +// a.Emptyf(obj, "error message %s", "formatted") func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -119,7 +119,7 @@ func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) // Equal asserts that two objects are equal. // -// a.Equal(123, 123) +// a.Equal(123, 123) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -134,8 +134,8 @@ func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs // EqualError asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // -// actualObj, err := SomeFunction() -// a.EqualError(err, expectedErrorString) +// actualObj, err := SomeFunction() +// a.EqualError(err, expectedErrorString) func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -146,8 +146,8 @@ func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ... // EqualErrorf asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // -// actualObj, err := SomeFunction() -// a.EqualErrorf(err, expectedErrorString, "error message %s", "formatted") +// actualObj, err := SomeFunction() +// a.EqualErrorf(err, expectedErrorString, "error message %s", "formatted") func (a *Assertions) EqualErrorf(theError error, errString string, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -155,10 +155,44 @@ func (a *Assertions) EqualErrorf(theError error, errString string, msg string, a return EqualErrorf(a.t, theError, errString, msg, args...) } +// EqualExportedValues asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// a.EqualExportedValues(S{1, 2}, S{1, 3}) => true +// a.EqualExportedValues(S{1, 2}, S{2, 3}) => false +func (a *Assertions) EqualExportedValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return EqualExportedValues(a.t, expected, actual, msgAndArgs...) +} + +// EqualExportedValuesf asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// a.EqualExportedValuesf(S{1, 2}, S{1, 3}, "error message %s", "formatted") => true +// a.EqualExportedValuesf(S{1, 2}, S{2, 3}, "error message %s", "formatted") => false +func (a *Assertions) EqualExportedValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return EqualExportedValuesf(a.t, expected, actual, msg, args...) +} + // EqualValues asserts that two objects are equal or convertable to the same types // and equal. // -// a.EqualValues(uint32(123), int32(123)) +// a.EqualValues(uint32(123), int32(123)) func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -169,7 +203,7 @@ func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAn // EqualValuesf asserts that two objects are equal or convertable to the same types // and equal. // -// a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted") +// a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted") func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -179,7 +213,7 @@ func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg // Equalf asserts that two objects are equal. // -// a.Equalf(123, 123, "error message %s", "formatted") +// a.Equalf(123, 123, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -193,10 +227,10 @@ func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string // Error asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if a.Error(err) { -// assert.Equal(t, expectedError, err) -// } +// actualObj, err := SomeFunction() +// if a.Error(err) { +// assert.Equal(t, expectedError, err) +// } func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -225,8 +259,8 @@ func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args .. // ErrorContains asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // -// actualObj, err := SomeFunction() -// a.ErrorContains(err, expectedErrorSubString) +// actualObj, err := SomeFunction() +// a.ErrorContains(err, expectedErrorSubString) func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -237,8 +271,8 @@ func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs . // ErrorContainsf asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // -// actualObj, err := SomeFunction() -// a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted") +// actualObj, err := SomeFunction() +// a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted") func (a *Assertions) ErrorContainsf(theError error, contains string, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -266,10 +300,10 @@ func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...inter // Errorf asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if a.Errorf(err, "error message %s", "formatted") { -// assert.Equal(t, expectedErrorf, err) -// } +// actualObj, err := SomeFunction() +// if a.Errorf(err, "error message %s", "formatted") { +// assert.Equal(t, expectedErrorf, err) +// } func (a *Assertions) Errorf(err error, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -280,7 +314,7 @@ func (a *Assertions) Errorf(err error, msg string, args ...interface{}) bool { // Eventually asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// a.Eventually(func() bool { return true; }, time.Second, 10*time.Millisecond) +// a.Eventually(func() bool { return true; }, time.Second, 10*time.Millisecond) func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -288,10 +322,60 @@ func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, ti return Eventually(a.t, condition, waitFor, tick, msgAndArgs...) } +// EventuallyWithT asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// a.EventuallyWithT(func(c *assert.CollectT) { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func (a *Assertions) EventuallyWithT(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return EventuallyWithT(a.t, condition, waitFor, tick, msgAndArgs...) +} + +// EventuallyWithTf asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// a.EventuallyWithTf(func(c *assert.CollectT, "error message %s", "formatted") { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func (a *Assertions) EventuallyWithTf(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + return EventuallyWithTf(a.t, condition, waitFor, tick, msg, args...) +} + // Eventuallyf asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// a.Eventuallyf(func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// a.Eventuallyf(func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func (a *Assertions) Eventuallyf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -301,7 +385,7 @@ func (a *Assertions) Eventuallyf(condition func() bool, waitFor time.Duration, t // Exactly asserts that two objects are equal in value and type. // -// a.Exactly(int32(123), int64(123)) +// a.Exactly(int32(123), int64(123)) func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -311,7 +395,7 @@ func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArg // Exactlyf asserts that two objects are equal in value and type. // -// a.Exactlyf(int32(123), int64(123), "error message %s", "formatted") +// a.Exactlyf(int32(123), int64(123), "error message %s", "formatted") func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -353,7 +437,7 @@ func (a *Assertions) Failf(failureMessage string, msg string, args ...interface{ // False asserts that the specified value is false. // -// a.False(myBool) +// a.False(myBool) func (a *Assertions) False(value bool, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -363,7 +447,7 @@ func (a *Assertions) False(value bool, msgAndArgs ...interface{}) bool { // Falsef asserts that the specified value is false. // -// a.Falsef(myBool, "error message %s", "formatted") +// a.Falsef(myBool, "error message %s", "formatted") func (a *Assertions) Falsef(value bool, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -391,9 +475,9 @@ func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) b // Greater asserts that the first element is greater than the second // -// a.Greater(2, 1) -// a.Greater(float64(2), float64(1)) -// a.Greater("b", "a") +// a.Greater(2, 1) +// a.Greater(float64(2), float64(1)) +// a.Greater("b", "a") func (a *Assertions) Greater(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -403,10 +487,10 @@ func (a *Assertions) Greater(e1 interface{}, e2 interface{}, msgAndArgs ...inter // GreaterOrEqual asserts that the first element is greater than or equal to the second // -// a.GreaterOrEqual(2, 1) -// a.GreaterOrEqual(2, 2) -// a.GreaterOrEqual("b", "a") -// a.GreaterOrEqual("b", "b") +// a.GreaterOrEqual(2, 1) +// a.GreaterOrEqual(2, 2) +// a.GreaterOrEqual("b", "a") +// a.GreaterOrEqual("b", "b") func (a *Assertions) GreaterOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -416,10 +500,10 @@ func (a *Assertions) GreaterOrEqual(e1 interface{}, e2 interface{}, msgAndArgs . // GreaterOrEqualf asserts that the first element is greater than or equal to the second // -// a.GreaterOrEqualf(2, 1, "error message %s", "formatted") -// a.GreaterOrEqualf(2, 2, "error message %s", "formatted") -// a.GreaterOrEqualf("b", "a", "error message %s", "formatted") -// a.GreaterOrEqualf("b", "b", "error message %s", "formatted") +// a.GreaterOrEqualf(2, 1, "error message %s", "formatted") +// a.GreaterOrEqualf(2, 2, "error message %s", "formatted") +// a.GreaterOrEqualf("b", "a", "error message %s", "formatted") +// a.GreaterOrEqualf("b", "b", "error message %s", "formatted") func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -429,9 +513,9 @@ func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string, // Greaterf asserts that the first element is greater than the second // -// a.Greaterf(2, 1, "error message %s", "formatted") -// a.Greaterf(float64(2), float64(1), "error message %s", "formatted") -// a.Greaterf("b", "a", "error message %s", "formatted") +// a.Greaterf(2, 1, "error message %s", "formatted") +// a.Greaterf(float64(2), float64(1), "error message %s", "formatted") +// a.Greaterf("b", "a", "error message %s", "formatted") func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -442,7 +526,7 @@ func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args . // HTTPBodyContains asserts that a specified handler returns a // body that contains a string. // -// a.HTTPBodyContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// a.HTTPBodyContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { @@ -455,7 +539,7 @@ func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, u // HTTPBodyContainsf asserts that a specified handler returns a // body that contains a string. // -// a.HTTPBodyContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// a.HTTPBodyContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { @@ -468,7 +552,7 @@ func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, // HTTPBodyNotContains asserts that a specified handler returns a // body that does not contain a string. // -// a.HTTPBodyNotContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// a.HTTPBodyNotContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { @@ -481,7 +565,7 @@ func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string // HTTPBodyNotContainsf asserts that a specified handler returns a // body that does not contain a string. // -// a.HTTPBodyNotContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// a.HTTPBodyNotContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { @@ -493,7 +577,7 @@ func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method strin // HTTPError asserts that a specified handler returns an error status code. // -// a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool { @@ -505,7 +589,7 @@ func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url stri // HTTPErrorf asserts that a specified handler returns an error status code. // -// a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { @@ -517,7 +601,7 @@ func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url str // HTTPRedirect asserts that a specified handler returns a redirect status code. // -// a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool { @@ -529,7 +613,7 @@ func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url s // HTTPRedirectf asserts that a specified handler returns a redirect status code. // -// a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { @@ -541,7 +625,7 @@ func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url // HTTPStatusCode asserts that a specified handler returns a specified status code. // -// a.HTTPStatusCode(myHandler, "GET", "/notImplemented", nil, 501) +// a.HTTPStatusCode(myHandler, "GET", "/notImplemented", nil, 501) // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) bool { @@ -553,7 +637,7 @@ func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url // HTTPStatusCodef asserts that a specified handler returns a specified status code. // -// a.HTTPStatusCodef(myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") +// a.HTTPStatusCodef(myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) bool { @@ -565,7 +649,7 @@ func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, ur // HTTPSuccess asserts that a specified handler returns a success status code. // -// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) +// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool { @@ -577,7 +661,7 @@ func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url st // HTTPSuccessf asserts that a specified handler returns a success status code. // -// a.HTTPSuccessf(myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") +// a.HTTPSuccessf(myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { @@ -589,7 +673,7 @@ func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url s // Implements asserts that an object is implemented by the specified interface. // -// a.Implements((*MyInterface)(nil), new(MyObject)) +// a.Implements((*MyInterface)(nil), new(MyObject)) func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -599,7 +683,7 @@ func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, // Implementsf asserts that an object is implemented by the specified interface. // -// a.Implementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted") +// a.Implementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted") func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -609,7 +693,7 @@ func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{} // InDelta asserts that the two numerals are within delta of each other. // -// a.InDelta(math.Pi, 22/7.0, 0.01) +// a.InDelta(math.Pi, 22/7.0, 0.01) func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -651,7 +735,7 @@ func (a *Assertions) InDeltaSlicef(expected interface{}, actual interface{}, del // InDeltaf asserts that the two numerals are within delta of each other. // -// a.InDeltaf(math.Pi, 22/7.0, 0.01, "error message %s", "formatted") +// a.InDeltaf(math.Pi, 22/7.0, 0.01, "error message %s", "formatted") func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -693,9 +777,9 @@ func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilo // IsDecreasing asserts that the collection is decreasing // -// a.IsDecreasing([]int{2, 1, 0}) -// a.IsDecreasing([]float{2, 1}) -// a.IsDecreasing([]string{"b", "a"}) +// a.IsDecreasing([]int{2, 1, 0}) +// a.IsDecreasing([]float{2, 1}) +// a.IsDecreasing([]string{"b", "a"}) func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -705,9 +789,9 @@ func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) // IsDecreasingf asserts that the collection is decreasing // -// a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted") -// a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted") -// a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted") +// a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted") +// a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted") +// a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted") func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -717,9 +801,9 @@ func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...inter // IsIncreasing asserts that the collection is increasing // -// a.IsIncreasing([]int{1, 2, 3}) -// a.IsIncreasing([]float{1, 2}) -// a.IsIncreasing([]string{"a", "b"}) +// a.IsIncreasing([]int{1, 2, 3}) +// a.IsIncreasing([]float{1, 2}) +// a.IsIncreasing([]string{"a", "b"}) func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -729,9 +813,9 @@ func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) // IsIncreasingf asserts that the collection is increasing // -// a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted") -// a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted") -// a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted") +// a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted") +// a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted") +// a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted") func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -741,9 +825,9 @@ func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...inter // IsNonDecreasing asserts that the collection is not decreasing // -// a.IsNonDecreasing([]int{1, 1, 2}) -// a.IsNonDecreasing([]float{1, 2}) -// a.IsNonDecreasing([]string{"a", "b"}) +// a.IsNonDecreasing([]int{1, 1, 2}) +// a.IsNonDecreasing([]float{1, 2}) +// a.IsNonDecreasing([]string{"a", "b"}) func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -753,9 +837,9 @@ func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface // IsNonDecreasingf asserts that the collection is not decreasing // -// a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted") -// a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted") -// a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted") +// a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted") +// a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted") +// a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted") func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -765,9 +849,9 @@ func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...in // IsNonIncreasing asserts that the collection is not increasing // -// a.IsNonIncreasing([]int{2, 1, 1}) -// a.IsNonIncreasing([]float{2, 1}) -// a.IsNonIncreasing([]string{"b", "a"}) +// a.IsNonIncreasing([]int{2, 1, 1}) +// a.IsNonIncreasing([]float{2, 1}) +// a.IsNonIncreasing([]string{"b", "a"}) func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -777,9 +861,9 @@ func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface // IsNonIncreasingf asserts that the collection is not increasing // -// a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted") -// a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted") -// a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted") +// a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted") +// a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted") +// a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted") func (a *Assertions) IsNonIncreasingf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -805,7 +889,7 @@ func (a *Assertions) IsTypef(expectedType interface{}, object interface{}, msg s // JSONEq asserts that two JSON strings are equivalent. // -// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) +// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -815,7 +899,7 @@ func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interf // JSONEqf asserts that two JSON strings are equivalent. // -// a.JSONEqf(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") +// a.JSONEqf(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") func (a *Assertions) JSONEqf(expected string, actual string, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -826,7 +910,7 @@ func (a *Assertions) JSONEqf(expected string, actual string, msg string, args .. // Len asserts that the specified object has specific length. // Len also fails if the object has a type that len() not accept. // -// a.Len(mySlice, 3) +// a.Len(mySlice, 3) func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -837,7 +921,7 @@ func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface // Lenf asserts that the specified object has specific length. // Lenf also fails if the object has a type that len() not accept. // -// a.Lenf(mySlice, 3, "error message %s", "formatted") +// a.Lenf(mySlice, 3, "error message %s", "formatted") func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -847,9 +931,9 @@ func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...in // Less asserts that the first element is less than the second // -// a.Less(1, 2) -// a.Less(float64(1), float64(2)) -// a.Less("a", "b") +// a.Less(1, 2) +// a.Less(float64(1), float64(2)) +// a.Less("a", "b") func (a *Assertions) Less(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -859,10 +943,10 @@ func (a *Assertions) Less(e1 interface{}, e2 interface{}, msgAndArgs ...interfac // LessOrEqual asserts that the first element is less than or equal to the second // -// a.LessOrEqual(1, 2) -// a.LessOrEqual(2, 2) -// a.LessOrEqual("a", "b") -// a.LessOrEqual("b", "b") +// a.LessOrEqual(1, 2) +// a.LessOrEqual(2, 2) +// a.LessOrEqual("a", "b") +// a.LessOrEqual("b", "b") func (a *Assertions) LessOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -872,10 +956,10 @@ func (a *Assertions) LessOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...i // LessOrEqualf asserts that the first element is less than or equal to the second // -// a.LessOrEqualf(1, 2, "error message %s", "formatted") -// a.LessOrEqualf(2, 2, "error message %s", "formatted") -// a.LessOrEqualf("a", "b", "error message %s", "formatted") -// a.LessOrEqualf("b", "b", "error message %s", "formatted") +// a.LessOrEqualf(1, 2, "error message %s", "formatted") +// a.LessOrEqualf(2, 2, "error message %s", "formatted") +// a.LessOrEqualf("a", "b", "error message %s", "formatted") +// a.LessOrEqualf("b", "b", "error message %s", "formatted") func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -885,9 +969,9 @@ func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, ar // Lessf asserts that the first element is less than the second // -// a.Lessf(1, 2, "error message %s", "formatted") -// a.Lessf(float64(1), float64(2), "error message %s", "formatted") -// a.Lessf("a", "b", "error message %s", "formatted") +// a.Lessf(1, 2, "error message %s", "formatted") +// a.Lessf(float64(1), float64(2), "error message %s", "formatted") +// a.Lessf("a", "b", "error message %s", "formatted") func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -897,8 +981,8 @@ func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...i // Negative asserts that the specified element is negative // -// a.Negative(-1) -// a.Negative(-1.23) +// a.Negative(-1) +// a.Negative(-1.23) func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -908,8 +992,8 @@ func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) bool { // Negativef asserts that the specified element is negative // -// a.Negativef(-1, "error message %s", "formatted") -// a.Negativef(-1.23, "error message %s", "formatted") +// a.Negativef(-1, "error message %s", "formatted") +// a.Negativef(-1.23, "error message %s", "formatted") func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -920,7 +1004,7 @@ func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) b // Never asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// a.Never(func() bool { return false; }, time.Second, 10*time.Millisecond) +// a.Never(func() bool { return false; }, time.Second, 10*time.Millisecond) func (a *Assertions) Never(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -931,7 +1015,7 @@ func (a *Assertions) Never(condition func() bool, waitFor time.Duration, tick ti // Neverf asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// a.Neverf(func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// a.Neverf(func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func (a *Assertions) Neverf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -941,7 +1025,7 @@ func (a *Assertions) Neverf(condition func() bool, waitFor time.Duration, tick t // Nil asserts that the specified object is nil. // -// a.Nil(err) +// a.Nil(err) func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -951,7 +1035,7 @@ func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) bool { // Nilf asserts that the specified object is nil. // -// a.Nilf(err, "error message %s", "formatted") +// a.Nilf(err, "error message %s", "formatted") func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -979,10 +1063,10 @@ func (a *Assertions) NoDirExistsf(path string, msg string, args ...interface{}) // NoError asserts that a function returned no error (i.e. `nil`). // -// actualObj, err := SomeFunction() -// if a.NoError(err) { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// if a.NoError(err) { +// assert.Equal(t, expectedObj, actualObj) +// } func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -992,10 +1076,10 @@ func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) bool { // NoErrorf asserts that a function returned no error (i.e. `nil`). // -// actualObj, err := SomeFunction() -// if a.NoErrorf(err, "error message %s", "formatted") { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// if a.NoErrorf(err, "error message %s", "formatted") { +// assert.Equal(t, expectedObj, actualObj) +// } func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1024,9 +1108,9 @@ func (a *Assertions) NoFileExistsf(path string, msg string, args ...interface{}) // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// a.NotContains("Hello World", "Earth") -// a.NotContains(["Hello", "World"], "Earth") -// a.NotContains({"Hello": "World"}, "Earth") +// a.NotContains("Hello World", "Earth") +// a.NotContains(["Hello", "World"], "Earth") +// a.NotContains({"Hello": "World"}, "Earth") func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1037,9 +1121,9 @@ func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs // NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// a.NotContainsf("Hello World", "Earth", "error message %s", "formatted") -// a.NotContainsf(["Hello", "World"], "Earth", "error message %s", "formatted") -// a.NotContainsf({"Hello": "World"}, "Earth", "error message %s", "formatted") +// a.NotContainsf("Hello World", "Earth", "error message %s", "formatted") +// a.NotContainsf(["Hello", "World"], "Earth", "error message %s", "formatted") +// a.NotContainsf({"Hello": "World"}, "Earth", "error message %s", "formatted") func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1050,9 +1134,9 @@ func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg strin // NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // -// if a.NotEmpty(obj) { -// assert.Equal(t, "two", obj[1]) -// } +// if a.NotEmpty(obj) { +// assert.Equal(t, "two", obj[1]) +// } func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1063,9 +1147,9 @@ func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) boo // NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // -// if a.NotEmptyf(obj, "error message %s", "formatted") { -// assert.Equal(t, "two", obj[1]) -// } +// if a.NotEmptyf(obj, "error message %s", "formatted") { +// assert.Equal(t, "two", obj[1]) +// } func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1075,7 +1159,7 @@ func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface // NotEqual asserts that the specified values are NOT equal. // -// a.NotEqual(obj1, obj2) +// a.NotEqual(obj1, obj2) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -1088,7 +1172,7 @@ func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndAr // NotEqualValues asserts that two objects are not equal even when converted to the same type // -// a.NotEqualValues(obj1, obj2) +// a.NotEqualValues(obj1, obj2) func (a *Assertions) NotEqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1098,7 +1182,7 @@ func (a *Assertions) NotEqualValues(expected interface{}, actual interface{}, ms // NotEqualValuesf asserts that two objects are not equal even when converted to the same type // -// a.NotEqualValuesf(obj1, obj2, "error message %s", "formatted") +// a.NotEqualValuesf(obj1, obj2, "error message %s", "formatted") func (a *Assertions) NotEqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1108,7 +1192,7 @@ func (a *Assertions) NotEqualValuesf(expected interface{}, actual interface{}, m // NotEqualf asserts that the specified values are NOT equal. // -// a.NotEqualf(obj1, obj2, "error message %s", "formatted") +// a.NotEqualf(obj1, obj2, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -1139,7 +1223,7 @@ func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...in // NotNil asserts that the specified object is not nil. // -// a.NotNil(err) +// a.NotNil(err) func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1149,7 +1233,7 @@ func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) bool // NotNilf asserts that the specified object is not nil. // -// a.NotNilf(err, "error message %s", "formatted") +// a.NotNilf(err, "error message %s", "formatted") func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1159,7 +1243,7 @@ func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{} // NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. // -// a.NotPanics(func(){ RemainCalm() }) +// a.NotPanics(func(){ RemainCalm() }) func (a *Assertions) NotPanics(f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1169,7 +1253,7 @@ func (a *Assertions) NotPanics(f PanicTestFunc, msgAndArgs ...interface{}) bool // NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. // -// a.NotPanicsf(func(){ RemainCalm() }, "error message %s", "formatted") +// a.NotPanicsf(func(){ RemainCalm() }, "error message %s", "formatted") func (a *Assertions) NotPanicsf(f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1179,8 +1263,8 @@ func (a *Assertions) NotPanicsf(f PanicTestFunc, msg string, args ...interface{} // NotRegexp asserts that a specified regexp does not match a string. // -// a.NotRegexp(regexp.MustCompile("starts"), "it's starting") -// a.NotRegexp("^start", "it's not starting") +// a.NotRegexp(regexp.MustCompile("starts"), "it's starting") +// a.NotRegexp("^start", "it's not starting") func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1190,8 +1274,8 @@ func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...in // NotRegexpf asserts that a specified regexp does not match a string. // -// a.NotRegexpf(regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") -// a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") +// a.NotRegexpf(regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") +// a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1201,7 +1285,7 @@ func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, arg // NotSame asserts that two pointers do not reference the same object. // -// a.NotSame(ptr1, ptr2) +// a.NotSame(ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1214,7 +1298,7 @@ func (a *Assertions) NotSame(expected interface{}, actual interface{}, msgAndArg // NotSamef asserts that two pointers do not reference the same object. // -// a.NotSamef(ptr1, ptr2, "error message %s", "formatted") +// a.NotSamef(ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1228,7 +1312,7 @@ func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg stri // NotSubset asserts that the specified list(array, slice...) contains not all // elements given in the specified subset(array, slice...). // -// a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") +// a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1239,7 +1323,7 @@ func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs // NotSubsetf asserts that the specified list(array, slice...) contains not all // elements given in the specified subset(array, slice...). // -// a.NotSubsetf([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") +// a.NotSubsetf([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1265,7 +1349,7 @@ func (a *Assertions) NotZerof(i interface{}, msg string, args ...interface{}) bo // Panics asserts that the code inside the specified PanicTestFunc panics. // -// a.Panics(func(){ GoCrazy() }) +// a.Panics(func(){ GoCrazy() }) func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1277,7 +1361,7 @@ func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool { // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// a.PanicsWithError("crazy error", func(){ GoCrazy() }) +// a.PanicsWithError("crazy error", func(){ GoCrazy() }) func (a *Assertions) PanicsWithError(errString string, f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1289,7 +1373,7 @@ func (a *Assertions) PanicsWithError(errString string, f PanicTestFunc, msgAndAr // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// a.PanicsWithErrorf("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// a.PanicsWithErrorf("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func (a *Assertions) PanicsWithErrorf(errString string, f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1300,7 +1384,7 @@ func (a *Assertions) PanicsWithErrorf(errString string, f PanicTestFunc, msg str // PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// a.PanicsWithValue("crazy error", func(){ GoCrazy() }) +// a.PanicsWithValue("crazy error", func(){ GoCrazy() }) func (a *Assertions) PanicsWithValue(expected interface{}, f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1311,7 +1395,7 @@ func (a *Assertions) PanicsWithValue(expected interface{}, f PanicTestFunc, msgA // PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// a.PanicsWithValuef("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// a.PanicsWithValuef("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func (a *Assertions) PanicsWithValuef(expected interface{}, f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1321,7 +1405,7 @@ func (a *Assertions) PanicsWithValuef(expected interface{}, f PanicTestFunc, msg // Panicsf asserts that the code inside the specified PanicTestFunc panics. // -// a.Panicsf(func(){ GoCrazy() }, "error message %s", "formatted") +// a.Panicsf(func(){ GoCrazy() }, "error message %s", "formatted") func (a *Assertions) Panicsf(f PanicTestFunc, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1331,8 +1415,8 @@ func (a *Assertions) Panicsf(f PanicTestFunc, msg string, args ...interface{}) b // Positive asserts that the specified element is positive // -// a.Positive(1) -// a.Positive(1.23) +// a.Positive(1) +// a.Positive(1.23) func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1342,8 +1426,8 @@ func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) bool { // Positivef asserts that the specified element is positive // -// a.Positivef(1, "error message %s", "formatted") -// a.Positivef(1.23, "error message %s", "formatted") +// a.Positivef(1, "error message %s", "formatted") +// a.Positivef(1.23, "error message %s", "formatted") func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1353,8 +1437,8 @@ func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) b // Regexp asserts that a specified regexp matches a string. // -// a.Regexp(regexp.MustCompile("start"), "it's starting") -// a.Regexp("start...$", "it's not starting") +// a.Regexp(regexp.MustCompile("start"), "it's starting") +// a.Regexp("start...$", "it's not starting") func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1364,8 +1448,8 @@ func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...inter // Regexpf asserts that a specified regexp matches a string. // -// a.Regexpf(regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") -// a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") +// a.Regexpf(regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") +// a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1375,7 +1459,7 @@ func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args . // Same asserts that two pointers reference the same object. // -// a.Same(ptr1, ptr2) +// a.Same(ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1388,7 +1472,7 @@ func (a *Assertions) Same(expected interface{}, actual interface{}, msgAndArgs . // Samef asserts that two pointers reference the same object. // -// a.Samef(ptr1, ptr2, "error message %s", "formatted") +// a.Samef(ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1402,7 +1486,7 @@ func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string, // Subset asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // -// a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") +// a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1413,7 +1497,7 @@ func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ... // Subsetf asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // -// a.Subsetf([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") +// a.Subsetf([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1423,7 +1507,7 @@ func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, a // True asserts that the specified value is true. // -// a.True(myBool) +// a.True(myBool) func (a *Assertions) True(value bool, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1433,7 +1517,7 @@ func (a *Assertions) True(value bool, msgAndArgs ...interface{}) bool { // Truef asserts that the specified value is true. // -// a.Truef(myBool, "error message %s", "formatted") +// a.Truef(myBool, "error message %s", "formatted") func (a *Assertions) Truef(value bool, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1443,7 +1527,7 @@ func (a *Assertions) Truef(value bool, msg string, args ...interface{}) bool { // WithinDuration asserts that the two times are within duration delta of each other. // -// a.WithinDuration(time.Now(), time.Now(), 10*time.Second) +// a.WithinDuration(time.Now(), time.Now(), 10*time.Second) func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1453,7 +1537,7 @@ func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta // WithinDurationf asserts that the two times are within duration delta of each other. // -// a.WithinDurationf(time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") +// a.WithinDurationf(time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1463,7 +1547,7 @@ func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta // WithinRange asserts that a time is within a time range (inclusive). // -// a.WithinRange(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +// a.WithinRange(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1473,7 +1557,7 @@ func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Tim // WithinRangef asserts that a time is within a time range (inclusive). // -// a.WithinRangef(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +// a.WithinRangef(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") func (a *Assertions) WithinRangef(actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/assert/assertion_order.go b/vendor/github.com/stretchr/testify/assert/assertion_order.go index 75944878..00df62a0 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_order.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_order.go @@ -46,36 +46,36 @@ func isOrdered(t TestingT, object interface{}, allowedComparesResults []CompareT // IsIncreasing asserts that the collection is increasing // -// assert.IsIncreasing(t, []int{1, 2, 3}) -// assert.IsIncreasing(t, []float{1, 2}) -// assert.IsIncreasing(t, []string{"a", "b"}) +// assert.IsIncreasing(t, []int{1, 2, 3}) +// assert.IsIncreasing(t, []float{1, 2}) +// assert.IsIncreasing(t, []string{"a", "b"}) func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { return isOrdered(t, object, []CompareType{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...) } // IsNonIncreasing asserts that the collection is not increasing // -// assert.IsNonIncreasing(t, []int{2, 1, 1}) -// assert.IsNonIncreasing(t, []float{2, 1}) -// assert.IsNonIncreasing(t, []string{"b", "a"}) +// assert.IsNonIncreasing(t, []int{2, 1, 1}) +// assert.IsNonIncreasing(t, []float{2, 1}) +// assert.IsNonIncreasing(t, []string{"b", "a"}) func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { return isOrdered(t, object, []CompareType{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...) } // IsDecreasing asserts that the collection is decreasing // -// assert.IsDecreasing(t, []int{2, 1, 0}) -// assert.IsDecreasing(t, []float{2, 1}) -// assert.IsDecreasing(t, []string{"b", "a"}) +// assert.IsDecreasing(t, []int{2, 1, 0}) +// assert.IsDecreasing(t, []float{2, 1}) +// assert.IsDecreasing(t, []string{"b", "a"}) func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { return isOrdered(t, object, []CompareType{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...) } // IsNonDecreasing asserts that the collection is not decreasing // -// assert.IsNonDecreasing(t, []int{1, 1, 2}) -// assert.IsNonDecreasing(t, []float{1, 2}) -// assert.IsNonDecreasing(t, []string{"a", "b"}) +// assert.IsNonDecreasing(t, []int{1, 1, 2}) +// assert.IsNonDecreasing(t, []float{1, 2}) +// assert.IsNonDecreasing(t, []string{"a", "b"}) func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { return isOrdered(t, object, []CompareType{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...) } diff --git a/vendor/github.com/stretchr/testify/assert/assertions.go b/vendor/github.com/stretchr/testify/assert/assertions.go index 2924cf3a..a55d1bba 100644 --- a/vendor/github.com/stretchr/testify/assert/assertions.go +++ b/vendor/github.com/stretchr/testify/assert/assertions.go @@ -75,6 +75,77 @@ func ObjectsAreEqual(expected, actual interface{}) bool { return bytes.Equal(exp, act) } +// copyExportedFields iterates downward through nested data structures and creates a copy +// that only contains the exported struct fields. +func copyExportedFields(expected interface{}) interface{} { + if isNil(expected) { + return expected + } + + expectedType := reflect.TypeOf(expected) + expectedKind := expectedType.Kind() + expectedValue := reflect.ValueOf(expected) + + switch expectedKind { + case reflect.Struct: + result := reflect.New(expectedType).Elem() + for i := 0; i < expectedType.NumField(); i++ { + field := expectedType.Field(i) + isExported := field.IsExported() + if isExported { + fieldValue := expectedValue.Field(i) + if isNil(fieldValue) || isNil(fieldValue.Interface()) { + continue + } + newValue := copyExportedFields(fieldValue.Interface()) + result.Field(i).Set(reflect.ValueOf(newValue)) + } + } + return result.Interface() + + case reflect.Ptr: + result := reflect.New(expectedType.Elem()) + unexportedRemoved := copyExportedFields(expectedValue.Elem().Interface()) + result.Elem().Set(reflect.ValueOf(unexportedRemoved)) + return result.Interface() + + case reflect.Array, reflect.Slice: + result := reflect.MakeSlice(expectedType, expectedValue.Len(), expectedValue.Len()) + for i := 0; i < expectedValue.Len(); i++ { + index := expectedValue.Index(i) + if isNil(index) { + continue + } + unexportedRemoved := copyExportedFields(index.Interface()) + result.Index(i).Set(reflect.ValueOf(unexportedRemoved)) + } + return result.Interface() + + case reflect.Map: + result := reflect.MakeMap(expectedType) + for _, k := range expectedValue.MapKeys() { + index := expectedValue.MapIndex(k) + unexportedRemoved := copyExportedFields(index.Interface()) + result.SetMapIndex(k, reflect.ValueOf(unexportedRemoved)) + } + return result.Interface() + + default: + return expected + } +} + +// ObjectsExportedFieldsAreEqual determines if the exported (public) fields of two objects are +// considered equal. This comparison of only exported fields is applied recursively to nested data +// structures. +// +// This function does no assertion of any kind. +func ObjectsExportedFieldsAreEqual(expected, actual interface{}) bool { + expectedCleaned := copyExportedFields(expected) + actualCleaned := copyExportedFields(actual) + return ObjectsAreEqualValues(expectedCleaned, actualCleaned) +} + // ObjectsAreEqualValues gets whether two objects are equal, or if their // values are equal. func ObjectsAreEqualValues(expected, actual interface{}) bool { @@ -271,7 +342,7 @@ type labeledContent struct { // labeledOutput returns a string consisting of the provided labeledContent. Each labeled output is appended in the following manner: // -// \t{{label}}:{{align_spaces}}\t{{content}}\n +// \t{{label}}:{{align_spaces}}\t{{content}}\n // // The initial carriage return is required to undo/erase any padding added by testing.T.Errorf. The "\t{{label}}:" is for the label. // If a label is shorter than the longest label provided, padding spaces are added to make all the labels match in length. Once this @@ -294,7 +365,7 @@ func labeledOutput(content ...labeledContent) string { // Implements asserts that an object is implemented by the specified interface. // -// assert.Implements(t, (*MyInterface)(nil), new(MyObject)) +// assert.Implements(t, (*MyInterface)(nil), new(MyObject)) func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -326,7 +397,7 @@ func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs // Equal asserts that two objects are equal. // -// assert.Equal(t, 123, 123) +// assert.Equal(t, 123, 123) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -367,7 +438,7 @@ func validateEqualArgs(expected, actual interface{}) error { // Same asserts that two pointers reference the same object. // -// assert.Same(t, ptr1, ptr2) +// assert.Same(t, ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -387,7 +458,7 @@ func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) b // NotSame asserts that two pointers do not reference the same object. // -// assert.NotSame(t, ptr1, ptr2) +// assert.NotSame(t, ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -455,7 +526,7 @@ func truncatingFormat(data interface{}) string { // EqualValues asserts that two objects are equal or convertable to the same types // and equal. // -// assert.EqualValues(t, uint32(123), int32(123)) +// assert.EqualValues(t, uint32(123), int32(123)) func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -473,9 +544,53 @@ func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interfa } +// EqualExportedValues asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// assert.EqualExportedValues(t, S{1, 2}, S{1, 3}) => true +// assert.EqualExportedValues(t, S{1, 2}, S{2, 3}) => false +func EqualExportedValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + aType := reflect.TypeOf(expected) + bType := reflect.TypeOf(actual) + + if aType != bType { + return Fail(t, fmt.Sprintf("Types expected to match exactly\n\t%v != %v", aType, bType), msgAndArgs...) + } + + if aType.Kind() != reflect.Struct { + return Fail(t, fmt.Sprintf("Types expected to both be struct \n\t%v != %v", aType.Kind(), reflect.Struct), msgAndArgs...) + } + + if bType.Kind() != reflect.Struct { + return Fail(t, fmt.Sprintf("Types expected to both be struct \n\t%v != %v", bType.Kind(), reflect.Struct), msgAndArgs...) + } + + expected = copyExportedFields(expected) + actual = copyExportedFields(actual) + + if !ObjectsAreEqualValues(expected, actual) { + diff := diff(expected, actual) + expected, actual = formatUnequalValues(expected, actual) + return Fail(t, fmt.Sprintf("Not equal (comparing only exported fields): \n"+ + "expected: %s\n"+ + "actual : %s%s", expected, actual, diff), msgAndArgs...) + } + + return true +} + // Exactly asserts that two objects are equal in value and type. // -// assert.Exactly(t, int32(123), int64(123)) +// assert.Exactly(t, int32(123), int64(123)) func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -494,7 +609,7 @@ func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{} // NotNil asserts that the specified object is not nil. // -// assert.NotNil(t, err) +// assert.NotNil(t, err) func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { if !isNil(object) { return true @@ -540,7 +655,7 @@ func isNil(object interface{}) bool { // Nil asserts that the specified object is nil. // -// assert.Nil(t, err) +// assert.Nil(t, err) func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { if isNil(object) { return true @@ -583,7 +698,7 @@ func isEmpty(object interface{}) bool { // Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // -// assert.Empty(t, obj) +// assert.Empty(t, obj) func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { pass := isEmpty(object) if !pass { @@ -600,9 +715,9 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { // NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // -// if assert.NotEmpty(t, obj) { -// assert.Equal(t, "two", obj[1]) -// } +// if assert.NotEmpty(t, obj) { +// assert.Equal(t, "two", obj[1]) +// } func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { pass := !isEmpty(object) if !pass { @@ -631,7 +746,7 @@ func getLen(x interface{}) (ok bool, length int) { // Len asserts that the specified object has specific length. // Len also fails if the object has a type that len() not accept. // -// assert.Len(t, mySlice, 3) +// assert.Len(t, mySlice, 3) func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -649,7 +764,7 @@ func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) // True asserts that the specified value is true. // -// assert.True(t, myBool) +// assert.True(t, myBool) func True(t TestingT, value bool, msgAndArgs ...interface{}) bool { if !value { if h, ok := t.(tHelper); ok { @@ -664,7 +779,7 @@ func True(t TestingT, value bool, msgAndArgs ...interface{}) bool { // False asserts that the specified value is false. // -// assert.False(t, myBool) +// assert.False(t, myBool) func False(t TestingT, value bool, msgAndArgs ...interface{}) bool { if value { if h, ok := t.(tHelper); ok { @@ -679,7 +794,7 @@ func False(t TestingT, value bool, msgAndArgs ...interface{}) bool { // NotEqual asserts that the specified values are NOT equal. // -// assert.NotEqual(t, obj1, obj2) +// assert.NotEqual(t, obj1, obj2) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -702,7 +817,7 @@ func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{ // NotEqualValues asserts that two objects are not equal even when converted to the same type // -// assert.NotEqualValues(t, obj1, obj2) +// assert.NotEqualValues(t, obj1, obj2) func NotEqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -761,9 +876,9 @@ func containsElement(list interface{}, element interface{}) (ok, found bool) { // Contains asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// assert.Contains(t, "Hello World", "World") -// assert.Contains(t, ["Hello", "World"], "World") -// assert.Contains(t, {"Hello": "World"}, "Hello") +// assert.Contains(t, "Hello World", "World") +// assert.Contains(t, ["Hello", "World"], "World") +// assert.Contains(t, {"Hello": "World"}, "Hello") func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -784,9 +899,9 @@ func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bo // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// assert.NotContains(t, "Hello World", "Earth") -// assert.NotContains(t, ["Hello", "World"], "Earth") -// assert.NotContains(t, {"Hello": "World"}, "Earth") +// assert.NotContains(t, "Hello World", "Earth") +// assert.NotContains(t, ["Hello", "World"], "Earth") +// assert.NotContains(t, {"Hello": "World"}, "Earth") func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -794,10 +909,10 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) ok, found := containsElement(s, contains) if !ok { - return Fail(t, fmt.Sprintf("\"%s\" could not be applied builtin len()", s), msgAndArgs...) + return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", s), msgAndArgs...) } if found { - return Fail(t, fmt.Sprintf("\"%s\" should not contain \"%s\"", s, contains), msgAndArgs...) + return Fail(t, fmt.Sprintf("%#v should not contain %#v", s, contains), msgAndArgs...) } return true @@ -807,7 +922,7 @@ func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) // Subset asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // -// assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") +// assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) { if h, ok := t.(tHelper); ok { h.Helper() @@ -863,7 +978,7 @@ func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok // NotSubset asserts that the specified list(array, slice...) contains not all // elements given in the specified subset(array, slice...). // -// assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") +// assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1048,7 +1163,7 @@ func didPanic(f PanicTestFunc) (didPanic bool, message interface{}, stack string // Panics asserts that the code inside the specified PanicTestFunc panics. // -// assert.Panics(t, func(){ GoCrazy() }) +// assert.Panics(t, func(){ GoCrazy() }) func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1064,7 +1179,7 @@ func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { // PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() }) +// assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() }) func PanicsWithValue(t TestingT, expected interface{}, f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1085,7 +1200,7 @@ func PanicsWithValue(t TestingT, expected interface{}, f PanicTestFunc, msgAndAr // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) +// assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) func PanicsWithError(t TestingT, errString string, f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1105,7 +1220,7 @@ func PanicsWithError(t TestingT, errString string, f PanicTestFunc, msgAndArgs . // NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. // -// assert.NotPanics(t, func(){ RemainCalm() }) +// assert.NotPanics(t, func(){ RemainCalm() }) func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1120,7 +1235,7 @@ func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { // WithinDuration asserts that the two times are within duration delta of each other. // -// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second) +// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second) func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1136,7 +1251,7 @@ func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration, // WithinRange asserts that a time is within a time range (inclusive). // -// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) func WithinRange(t TestingT, actual, start, end time.Time, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1195,7 +1310,7 @@ func toFloat(x interface{}) (float64, bool) { // InDelta asserts that the two numerals are within delta of each other. // -// assert.InDelta(t, math.Pi, 22/7.0, 0.01) +// assert.InDelta(t, math.Pi, 22/7.0, 0.01) func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1368,10 +1483,10 @@ func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, m // NoError asserts that a function returned no error (i.e. `nil`). // -// actualObj, err := SomeFunction() -// if assert.NoError(t, err) { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// if assert.NoError(t, err) { +// assert.Equal(t, expectedObj, actualObj) +// } func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool { if err != nil { if h, ok := t.(tHelper); ok { @@ -1385,10 +1500,10 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool { // Error asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if assert.Error(t, err) { -// assert.Equal(t, expectedError, err) -// } +// actualObj, err := SomeFunction() +// if assert.Error(t, err) { +// assert.Equal(t, expectedError, err) +// } func Error(t TestingT, err error, msgAndArgs ...interface{}) bool { if err == nil { if h, ok := t.(tHelper); ok { @@ -1403,8 +1518,8 @@ func Error(t TestingT, err error, msgAndArgs ...interface{}) bool { // EqualError asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // -// actualObj, err := SomeFunction() -// assert.EqualError(t, err, expectedErrorString) +// actualObj, err := SomeFunction() +// assert.EqualError(t, err, expectedErrorString) func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1426,8 +1541,8 @@ func EqualError(t TestingT, theError error, errString string, msgAndArgs ...inte // ErrorContains asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // -// actualObj, err := SomeFunction() -// assert.ErrorContains(t, err, expectedErrorSubString) +// actualObj, err := SomeFunction() +// assert.ErrorContains(t, err, expectedErrorSubString) func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1460,8 +1575,8 @@ func matchRegexp(rx interface{}, str interface{}) bool { // Regexp asserts that a specified regexp matches a string. // -// assert.Regexp(t, regexp.MustCompile("start"), "it's starting") -// assert.Regexp(t, "start...$", "it's not starting") +// assert.Regexp(t, regexp.MustCompile("start"), "it's starting") +// assert.Regexp(t, "start...$", "it's not starting") func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1478,8 +1593,8 @@ func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface // NotRegexp asserts that a specified regexp does not match a string. // -// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") -// assert.NotRegexp(t, "^start", "it's not starting") +// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") +// assert.NotRegexp(t, "^start", "it's not starting") func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1591,7 +1706,7 @@ func NoDirExists(t TestingT, path string, msgAndArgs ...interface{}) bool { // JSONEq asserts that two JSON strings are equivalent. // -// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) +// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1714,7 +1829,7 @@ type tHelper interface { // Eventually asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) +// assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() @@ -1744,10 +1859,93 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t } } +// CollectT implements the TestingT interface and collects all errors. +type CollectT struct { + errors []error +} + +// Errorf collects the error. +func (c *CollectT) Errorf(format string, args ...interface{}) { + c.errors = append(c.errors, fmt.Errorf(format, args...)) +} + +// FailNow panics. +func (c *CollectT) FailNow() { + panic("Assertion failed") +} + +// Reset clears the collected errors. +func (c *CollectT) Reset() { + c.errors = nil +} + +// Copy copies the collected errors to the supplied t. +func (c *CollectT) Copy(t TestingT) { + if tt, ok := t.(tHelper); ok { + tt.Helper() + } + for _, err := range c.errors { + t.Errorf("%v", err) + } +} + +// EventuallyWithT asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// assert.EventuallyWithT(t, func(c *assert.CollectT) { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func EventuallyWithT(t TestingT, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { + if h, ok := t.(tHelper); ok { + h.Helper() + } + + collect := new(CollectT) + ch := make(chan bool, 1) + + timer := time.NewTimer(waitFor) + defer timer.Stop() + + ticker := time.NewTicker(tick) + defer ticker.Stop() + + for tick := ticker.C; ; { + select { + case <-timer.C: + collect.Copy(t) + return Fail(t, "Condition never satisfied", msgAndArgs...) + case <-tick: + tick = nil + collect.Reset() + go func() { + condition(collect) + ch <- len(collect.errors) == 0 + }() + case v := <-ch: + if v { + return true + } + tick = ticker.C + } + } +} + // Never asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) +// assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/assert/doc.go b/vendor/github.com/stretchr/testify/assert/doc.go index c9dccc4d..4953981d 100644 --- a/vendor/github.com/stretchr/testify/assert/doc.go +++ b/vendor/github.com/stretchr/testify/assert/doc.go @@ -1,39 +1,40 @@ // Package assert provides a set of comprehensive testing tools for use with the normal Go testing system. // -// Example Usage +// # Example Usage // // The following is a complete example using assert in a standard test function: -// import ( -// "testing" -// "github.com/stretchr/testify/assert" -// ) // -// func TestSomething(t *testing.T) { +// import ( +// "testing" +// "github.com/stretchr/testify/assert" +// ) // -// var a string = "Hello" -// var b string = "Hello" +// func TestSomething(t *testing.T) { // -// assert.Equal(t, a, b, "The two words should be the same.") +// var a string = "Hello" +// var b string = "Hello" // -// } +// assert.Equal(t, a, b, "The two words should be the same.") +// +// } // // if you assert many times, use the format below: // -// import ( -// "testing" -// "github.com/stretchr/testify/assert" -// ) +// import ( +// "testing" +// "github.com/stretchr/testify/assert" +// ) // -// func TestSomething(t *testing.T) { -// assert := assert.New(t) +// func TestSomething(t *testing.T) { +// assert := assert.New(t) // -// var a string = "Hello" -// var b string = "Hello" +// var a string = "Hello" +// var b string = "Hello" // -// assert.Equal(a, b, "The two words should be the same.") -// } +// assert.Equal(a, b, "The two words should be the same.") +// } // -// Assertions +// # Assertions // // Assertions allow you to easily write test code, and are global funcs in the `assert` package. // All assertion functions take, as the first argument, the `*testing.T` object provided by the diff --git a/vendor/github.com/stretchr/testify/assert/http_assertions.go b/vendor/github.com/stretchr/testify/assert/http_assertions.go index 4ed341dd..d8038c28 100644 --- a/vendor/github.com/stretchr/testify/assert/http_assertions.go +++ b/vendor/github.com/stretchr/testify/assert/http_assertions.go @@ -23,7 +23,7 @@ func httpCode(handler http.HandlerFunc, method, url string, values url.Values) ( // HTTPSuccess asserts that a specified handler returns a success status code. // -// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) +// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) // // Returns whether the assertion was successful (true) or not (false). func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { @@ -45,7 +45,7 @@ func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, value // HTTPRedirect asserts that a specified handler returns a redirect status code. // -// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { @@ -67,7 +67,7 @@ func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, valu // HTTPError asserts that a specified handler returns an error status code. // -// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { @@ -89,7 +89,7 @@ func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values // HTTPStatusCode asserts that a specified handler returns a specified status code. // -// assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) +// assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) // // Returns whether the assertion was successful (true) or not (false). func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) bool { @@ -124,7 +124,7 @@ func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) s // HTTPBodyContains asserts that a specified handler returns a // body that contains a string. // -// assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { @@ -144,7 +144,7 @@ func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, // HTTPBodyNotContains asserts that a specified handler returns a // body that does not contain a string. // -// assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { diff --git a/vendor/github.com/stretchr/testify/mock/doc.go b/vendor/github.com/stretchr/testify/mock/doc.go index 7324128e..d6b3c844 100644 --- a/vendor/github.com/stretchr/testify/mock/doc.go +++ b/vendor/github.com/stretchr/testify/mock/doc.go @@ -1,17 +1,17 @@ // Package mock provides a system by which it is possible to mock your objects // and verify calls are happening as expected. // -// Example Usage +// # Example Usage // // The mock package provides an object, Mock, that tracks activity on another object. It is usually // embedded into a test object as shown below: // -// type MyTestObject struct { -// // add a Mock object instance -// mock.Mock +// type MyTestObject struct { +// // add a Mock object instance +// mock.Mock // -// // other fields go here as normal -// } +// // other fields go here as normal +// } // // When implementing the methods of an interface, you wire your functions up // to call the Mock.Called(args...) method, and return the appropriate values. @@ -19,25 +19,25 @@ // For example, to mock a method that saves the name and age of a person and returns // the year of their birth or an error, you might write this: // -// func (o *MyTestObject) SavePersonDetails(firstname, lastname string, age int) (int, error) { -// args := o.Called(firstname, lastname, age) -// return args.Int(0), args.Error(1) -// } +// func (o *MyTestObject) SavePersonDetails(firstname, lastname string, age int) (int, error) { +// args := o.Called(firstname, lastname, age) +// return args.Int(0), args.Error(1) +// } // // The Int, Error and Bool methods are examples of strongly typed getters that take the argument // index position. Given this argument list: // -// (12, true, "Something") +// (12, true, "Something") // // You could read them out strongly typed like this: // -// args.Int(0) -// args.Bool(1) -// args.String(2) +// args.Int(0) +// args.Bool(1) +// args.String(2) // // For objects of your own type, use the generic Arguments.Get(index) method and make a type assertion: // -// return args.Get(0).(*MyObject), args.Get(1).(*AnotherObjectOfMine) +// return args.Get(0).(*MyObject), args.Get(1).(*AnotherObjectOfMine) // // This may cause a panic if the object you are getting is nil (the type assertion will fail), in those // cases you should check for nil first. diff --git a/vendor/github.com/stretchr/testify/mock/mock.go b/vendor/github.com/stretchr/testify/mock/mock.go index e6ff8dfe..f4b42e44 100644 --- a/vendor/github.com/stretchr/testify/mock/mock.go +++ b/vendor/github.com/stretchr/testify/mock/mock.go @@ -3,6 +3,7 @@ package mock import ( "errors" "fmt" + "path" "reflect" "regexp" "runtime" @@ -13,6 +14,7 @@ import ( "github.com/davecgh/go-spew/spew" "github.com/pmezard/go-difflib/difflib" "github.com/stretchr/objx" + "github.com/stretchr/testify/assert" ) @@ -99,7 +101,7 @@ func (c *Call) unlock() { // Return specifies the return arguments for the expectation. // -// Mock.On("DoSomething").Return(errors.New("failed")) +// Mock.On("DoSomething").Return(errors.New("failed")) func (c *Call) Return(returnArguments ...interface{}) *Call { c.lock() defer c.unlock() @@ -111,7 +113,7 @@ func (c *Call) Return(returnArguments ...interface{}) *Call { // Panic specifies if the functon call should fail and the panic message // -// Mock.On("DoSomething").Panic("test panic") +// Mock.On("DoSomething").Panic("test panic") func (c *Call) Panic(msg string) *Call { c.lock() defer c.unlock() @@ -123,14 +125,14 @@ func (c *Call) Panic(msg string) *Call { // Once indicates that that the mock should only return the value once. // -// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Once() +// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Once() func (c *Call) Once() *Call { return c.Times(1) } // Twice indicates that that the mock should only return the value twice. // -// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Twice() +// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Twice() func (c *Call) Twice() *Call { return c.Times(2) } @@ -138,7 +140,7 @@ func (c *Call) Twice() *Call { // Times indicates that that the mock should only return the indicated number // of times. // -// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Times(5) +// Mock.On("MyMethod", arg1, arg2).Return(returnArg1, returnArg2).Times(5) func (c *Call) Times(i int) *Call { c.lock() defer c.unlock() @@ -149,7 +151,7 @@ func (c *Call) Times(i int) *Call { // WaitUntil sets the channel that will block the mock's return until its closed // or a message is received. // -// Mock.On("MyMethod", arg1, arg2).WaitUntil(time.After(time.Second)) +// Mock.On("MyMethod", arg1, arg2).WaitUntil(time.After(time.Second)) func (c *Call) WaitUntil(w <-chan time.Time) *Call { c.lock() defer c.unlock() @@ -159,7 +161,7 @@ func (c *Call) WaitUntil(w <-chan time.Time) *Call { // After sets how long to block until the call returns // -// Mock.On("MyMethod", arg1, arg2).After(time.Second) +// Mock.On("MyMethod", arg1, arg2).After(time.Second) func (c *Call) After(d time.Duration) *Call { c.lock() defer c.unlock() @@ -171,10 +173,10 @@ func (c *Call) After(d time.Duration) *Call { // mocking a method (such as an unmarshaler) that takes a pointer to a struct and // sets properties in such struct // -// Mock.On("Unmarshal", AnythingOfType("*map[string]interface{}")).Return().Run(func(args Arguments) { -// arg := args.Get(0).(*map[string]interface{}) -// arg["foo"] = "bar" -// }) +// Mock.On("Unmarshal", AnythingOfType("*map[string]interface{}")).Return().Run(func(args Arguments) { +// arg := args.Get(0).(*map[string]interface{}) +// arg["foo"] = "bar" +// }) func (c *Call) Run(fn func(args Arguments)) *Call { c.lock() defer c.unlock() @@ -194,16 +196,18 @@ func (c *Call) Maybe() *Call { // On chains a new expectation description onto the mocked interface. This // allows syntax like. // -// Mock. -// On("MyMethod", 1).Return(nil). -// On("MyOtherMethod", 'a', 'b', 'c').Return(errors.New("Some Error")) +// Mock. +// On("MyMethod", 1).Return(nil). +// On("MyOtherMethod", 'a', 'b', 'c').Return(errors.New("Some Error")) +// //go:noinline func (c *Call) On(methodName string, arguments ...interface{}) *Call { return c.Parent.On(methodName, arguments...) } // Unset removes a mock handler from being called. -// test.On("func", mock.Anything).Unset() +// +// test.On("func", mock.Anything).Unset() func (c *Call) Unset() *Call { var unlockOnce sync.Once @@ -249,9 +253,9 @@ func (c *Call) Unset() *Call { // calls have been called as expected. The referenced calls may be from the // same mock instance and/or other mock instances. // -// Mock.On("Do").Return(nil).Notbefore( -// Mock.On("Init").Return(nil) -// ) +// Mock.On("Do").Return(nil).Notbefore( +// Mock.On("Init").Return(nil) +// ) func (c *Call) NotBefore(calls ...*Call) *Call { c.lock() defer c.unlock() @@ -334,7 +338,7 @@ func (m *Mock) fail(format string, args ...interface{}) { // On starts a description of an expectation of the specified method // being called. // -// Mock.On("MyMethod", arg1, arg2) +// Mock.On("MyMethod", arg1, arg2) func (m *Mock) On(methodName string, arguments ...interface{}) *Call { for _, arg := range arguments { if v := reflect.ValueOf(arg); v.Kind() == reflect.Func { @@ -424,6 +428,10 @@ func callString(method string, arguments Arguments, includeArgumentValues bool) if includeArgumentValues { var argVals []string for argIndex, arg := range arguments { + if _, ok := arg.(*FunctionalOptionsArgument); ok { + argVals = append(argVals, fmt.Sprintf("%d: %s", argIndex, arg)) + continue + } argVals = append(argVals, fmt.Sprintf("%d: %#v", argIndex, arg)) } argValsString = fmt.Sprintf("\n\t\t%s", strings.Join(argVals, "\n\t\t")) @@ -758,6 +766,7 @@ type AnythingOfTypeArgument string // name of the type to check for. Used in Diff and Assert. // // For example: +// // Assert(t, AnythingOfType("string"), AnythingOfType("int")) func AnythingOfType(t string) AnythingOfTypeArgument { return AnythingOfTypeArgument(t) @@ -780,6 +789,34 @@ func IsType(t interface{}) *IsTypeArgument { return &IsTypeArgument{t: t} } +// FunctionalOptionsArgument is a struct that contains the type and value of an functional option argument +// for use when type checking. +type FunctionalOptionsArgument struct { + value interface{} +} + +// String returns the string representation of FunctionalOptionsArgument +func (f *FunctionalOptionsArgument) String() string { + var name string + tValue := reflect.ValueOf(f.value) + if tValue.Len() > 0 { + name = "[]" + reflect.TypeOf(tValue.Index(0).Interface()).String() + } + + return strings.Replace(fmt.Sprintf("%#v", f.value), "[]interface {}", name, 1) +} + +// FunctionalOptions returns an FunctionalOptionsArgument object containing the functional option type +// and the values to check of +// +// For example: +// Assert(t, FunctionalOptions("[]foo.FunctionalOption", foo.Opt1(), foo.Opt2())) +func FunctionalOptions(value ...interface{}) *FunctionalOptionsArgument { + return &FunctionalOptionsArgument{ + value: value, + } +} + // argumentMatcher performs custom argument matching, returning whether or // not the argument is matched by the expectation fixture function. type argumentMatcher struct { @@ -926,6 +963,29 @@ func (args Arguments) Diff(objects []interface{}) (string, int) { differences++ output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, reflect.TypeOf(t).Name(), reflect.TypeOf(actual).Name(), actualFmt) } + } else if reflect.TypeOf(expected) == reflect.TypeOf((*FunctionalOptionsArgument)(nil)) { + t := expected.(*FunctionalOptionsArgument).value + + var name string + tValue := reflect.ValueOf(t) + if tValue.Len() > 0 { + name = "[]" + reflect.TypeOf(tValue.Index(0).Interface()).String() + } + + tName := reflect.TypeOf(t).Name() + if name != reflect.TypeOf(actual).String() && tValue.Len() != 0 { + differences++ + output = fmt.Sprintf("%s\t%d: FAIL: type %s != type %s - %s\n", output, i, tName, reflect.TypeOf(actual).Name(), actualFmt) + } else { + if ef, af := assertOpts(t, actual); ef == "" && af == "" { + // match + output = fmt.Sprintf("%s\t%d: PASS: %s == %s\n", output, i, tName, tName) + } else { + // not match + differences++ + output = fmt.Sprintf("%s\t%d: FAIL: %s != %s\n", output, i, af, ef) + } + } } else { // normal checking @@ -1102,3 +1162,65 @@ var spewConfig = spew.ConfigState{ type tHelper interface { Helper() } + +func assertOpts(expected, actual interface{}) (expectedFmt, actualFmt string) { + expectedOpts := reflect.ValueOf(expected) + actualOpts := reflect.ValueOf(actual) + var expectedNames []string + for i := 0; i < expectedOpts.Len(); i++ { + expectedNames = append(expectedNames, funcName(expectedOpts.Index(i).Interface())) + } + var actualNames []string + for i := 0; i < actualOpts.Len(); i++ { + actualNames = append(actualNames, funcName(actualOpts.Index(i).Interface())) + } + if !assert.ObjectsAreEqual(expectedNames, actualNames) { + expectedFmt = fmt.Sprintf("%v", expectedNames) + actualFmt = fmt.Sprintf("%v", actualNames) + return + } + + for i := 0; i < expectedOpts.Len(); i++ { + expectedOpt := expectedOpts.Index(i).Interface() + actualOpt := actualOpts.Index(i).Interface() + + expectedFunc := expectedNames[i] + actualFunc := actualNames[i] + if expectedFunc != actualFunc { + expectedFmt = expectedFunc + actualFmt = actualFunc + return + } + + ot := reflect.TypeOf(expectedOpt) + var expectedValues []reflect.Value + var actualValues []reflect.Value + if ot.NumIn() == 0 { + return + } + + for i := 0; i < ot.NumIn(); i++ { + vt := ot.In(i).Elem() + expectedValues = append(expectedValues, reflect.New(vt)) + actualValues = append(actualValues, reflect.New(vt)) + } + + reflect.ValueOf(expectedOpt).Call(expectedValues) + reflect.ValueOf(actualOpt).Call(actualValues) + + for i := 0; i < ot.NumIn(); i++ { + if !assert.ObjectsAreEqual(expectedValues[i].Interface(), actualValues[i].Interface()) { + expectedFmt = fmt.Sprintf("%s %+v", expectedNames[i], expectedValues[i].Interface()) + actualFmt = fmt.Sprintf("%s %+v", expectedNames[i], actualValues[i].Interface()) + return + } + } + } + + return "", "" +} + +func funcName(opt interface{}) string { + n := runtime.FuncForPC(reflect.ValueOf(opt).Pointer()).Name() + return strings.TrimSuffix(path.Base(n), path.Ext(n)) +} diff --git a/vendor/github.com/stretchr/testify/require/doc.go b/vendor/github.com/stretchr/testify/require/doc.go index 169de392..96843472 100644 --- a/vendor/github.com/stretchr/testify/require/doc.go +++ b/vendor/github.com/stretchr/testify/require/doc.go @@ -1,24 +1,25 @@ // Package require implements the same assertions as the `assert` package but // stops test execution when a test fails. // -// Example Usage +// # Example Usage // // The following is a complete example using require in a standard test function: -// import ( -// "testing" -// "github.com/stretchr/testify/require" -// ) // -// func TestSomething(t *testing.T) { +// import ( +// "testing" +// "github.com/stretchr/testify/require" +// ) // -// var a string = "Hello" -// var b string = "Hello" +// func TestSomething(t *testing.T) { // -// require.Equal(t, a, b, "The two words should be the same.") +// var a string = "Hello" +// var b string = "Hello" // -// } +// require.Equal(t, a, b, "The two words should be the same.") // -// Assertions +// } +// +// # Assertions // // The `require` package have same global functions as in the `assert` package, // but instead of returning a boolean result they call `t.FailNow()`. diff --git a/vendor/github.com/stretchr/testify/require/require.go b/vendor/github.com/stretchr/testify/require/require.go index 880853f5..63f85214 100644 --- a/vendor/github.com/stretchr/testify/require/require.go +++ b/vendor/github.com/stretchr/testify/require/require.go @@ -37,9 +37,9 @@ func Conditionf(t TestingT, comp assert.Comparison, msg string, args ...interfac // Contains asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// assert.Contains(t, "Hello World", "World") -// assert.Contains(t, ["Hello", "World"], "World") -// assert.Contains(t, {"Hello": "World"}, "Hello") +// assert.Contains(t, "Hello World", "World") +// assert.Contains(t, ["Hello", "World"], "World") +// assert.Contains(t, {"Hello": "World"}, "Hello") func Contains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -53,9 +53,9 @@ func Contains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...int // Containsf asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// assert.Containsf(t, "Hello World", "World", "error message %s", "formatted") -// assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted") -// assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted") +// assert.Containsf(t, "Hello World", "World", "error message %s", "formatted") +// assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted") +// assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted") func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -123,7 +123,7 @@ func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string // Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // -// assert.Empty(t, obj) +// assert.Empty(t, obj) func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -137,7 +137,7 @@ func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) { // Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // -// assert.Emptyf(t, obj, "error message %s", "formatted") +// assert.Emptyf(t, obj, "error message %s", "formatted") func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -150,7 +150,7 @@ func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) { // Equal asserts that two objects are equal. // -// assert.Equal(t, 123, 123) +// assert.Equal(t, 123, 123) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -168,8 +168,8 @@ func Equal(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...i // EqualError asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // -// actualObj, err := SomeFunction() -// assert.EqualError(t, err, expectedErrorString) +// actualObj, err := SomeFunction() +// assert.EqualError(t, err, expectedErrorString) func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -183,8 +183,8 @@ func EqualError(t TestingT, theError error, errString string, msgAndArgs ...inte // EqualErrorf asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // -// actualObj, err := SomeFunction() -// assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted") +// actualObj, err := SomeFunction() +// assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted") func EqualErrorf(t TestingT, theError error, errString string, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -195,10 +195,50 @@ func EqualErrorf(t TestingT, theError error, errString string, msg string, args t.FailNow() } +// EqualExportedValues asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// assert.EqualExportedValues(t, S{1, 2}, S{1, 3}) => true +// assert.EqualExportedValues(t, S{1, 2}, S{2, 3}) => false +func EqualExportedValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.EqualExportedValues(t, expected, actual, msgAndArgs...) { + return + } + t.FailNow() +} + +// EqualExportedValuesf asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// assert.EqualExportedValuesf(t, S{1, 2}, S{1, 3}, "error message %s", "formatted") => true +// assert.EqualExportedValuesf(t, S{1, 2}, S{2, 3}, "error message %s", "formatted") => false +func EqualExportedValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.EqualExportedValuesf(t, expected, actual, msg, args...) { + return + } + t.FailNow() +} + // EqualValues asserts that two objects are equal or convertable to the same types // and equal. // -// assert.EqualValues(t, uint32(123), int32(123)) +// assert.EqualValues(t, uint32(123), int32(123)) func EqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -212,7 +252,7 @@ func EqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArg // EqualValuesf asserts that two objects are equal or convertable to the same types // and equal. // -// assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") +// assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -225,7 +265,7 @@ func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg stri // Equalf asserts that two objects are equal. // -// assert.Equalf(t, 123, 123, "error message %s", "formatted") +// assert.Equalf(t, 123, 123, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -242,10 +282,10 @@ func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, ar // Error asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if assert.Error(t, err) { -// assert.Equal(t, expectedError, err) -// } +// actualObj, err := SomeFunction() +// if assert.Error(t, err) { +// assert.Equal(t, expectedError, err) +// } func Error(t TestingT, err error, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -283,8 +323,8 @@ func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...int // ErrorContains asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // -// actualObj, err := SomeFunction() -// assert.ErrorContains(t, err, expectedErrorSubString) +// actualObj, err := SomeFunction() +// assert.ErrorContains(t, err, expectedErrorSubString) func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -298,8 +338,8 @@ func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...in // ErrorContainsf asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // -// actualObj, err := SomeFunction() -// assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") +// actualObj, err := SomeFunction() +// assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") func ErrorContainsf(t TestingT, theError error, contains string, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -336,10 +376,10 @@ func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface // Errorf asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if assert.Errorf(t, err, "error message %s", "formatted") { -// assert.Equal(t, expectedErrorf, err) -// } +// actualObj, err := SomeFunction() +// if assert.Errorf(t, err, "error message %s", "formatted") { +// assert.Equal(t, expectedErrorf, err) +// } func Errorf(t TestingT, err error, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -353,7 +393,7 @@ func Errorf(t TestingT, err error, msg string, args ...interface{}) { // Eventually asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) +// assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -364,10 +404,66 @@ func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick t t.FailNow() } +// EventuallyWithT asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// assert.EventuallyWithT(t, func(c *assert.CollectT) { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func EventuallyWithT(t TestingT, condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.EventuallyWithT(t, condition, waitFor, tick, msgAndArgs...) { + return + } + t.FailNow() +} + +// EventuallyWithTf asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// assert.EventuallyWithTf(t, func(c *assert.CollectT, "error message %s", "formatted") { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func EventuallyWithTf(t TestingT, condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { + if h, ok := t.(tHelper); ok { + h.Helper() + } + if assert.EventuallyWithTf(t, condition, waitFor, tick, msg, args...) { + return + } + t.FailNow() +} + // Eventuallyf asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -380,7 +476,7 @@ func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick // Exactly asserts that two objects are equal in value and type. // -// assert.Exactly(t, int32(123), int64(123)) +// assert.Exactly(t, int32(123), int64(123)) func Exactly(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -393,7 +489,7 @@ func Exactly(t TestingT, expected interface{}, actual interface{}, msgAndArgs .. // Exactlyf asserts that two objects are equal in value and type. // -// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted") +// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted") func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -450,7 +546,7 @@ func Failf(t TestingT, failureMessage string, msg string, args ...interface{}) { // False asserts that the specified value is false. // -// assert.False(t, myBool) +// assert.False(t, myBool) func False(t TestingT, value bool, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -463,7 +559,7 @@ func False(t TestingT, value bool, msgAndArgs ...interface{}) { // Falsef asserts that the specified value is false. // -// assert.Falsef(t, myBool, "error message %s", "formatted") +// assert.Falsef(t, myBool, "error message %s", "formatted") func Falsef(t TestingT, value bool, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -500,9 +596,9 @@ func FileExistsf(t TestingT, path string, msg string, args ...interface{}) { // Greater asserts that the first element is greater than the second // -// assert.Greater(t, 2, 1) -// assert.Greater(t, float64(2), float64(1)) -// assert.Greater(t, "b", "a") +// assert.Greater(t, 2, 1) +// assert.Greater(t, float64(2), float64(1)) +// assert.Greater(t, "b", "a") func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -515,10 +611,10 @@ func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface // GreaterOrEqual asserts that the first element is greater than or equal to the second // -// assert.GreaterOrEqual(t, 2, 1) -// assert.GreaterOrEqual(t, 2, 2) -// assert.GreaterOrEqual(t, "b", "a") -// assert.GreaterOrEqual(t, "b", "b") +// assert.GreaterOrEqual(t, 2, 1) +// assert.GreaterOrEqual(t, 2, 2) +// assert.GreaterOrEqual(t, "b", "a") +// assert.GreaterOrEqual(t, "b", "b") func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -531,10 +627,10 @@ func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...in // GreaterOrEqualf asserts that the first element is greater than or equal to the second // -// assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted") -// assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted") -// assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted") -// assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted") +// assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted") +// assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted") +// assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted") +// assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted") func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -547,9 +643,9 @@ func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, arg // Greaterf asserts that the first element is greater than the second // -// assert.Greaterf(t, 2, 1, "error message %s", "formatted") -// assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted") -// assert.Greaterf(t, "b", "a", "error message %s", "formatted") +// assert.Greaterf(t, 2, 1, "error message %s", "formatted") +// assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted") +// assert.Greaterf(t, "b", "a", "error message %s", "formatted") func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -563,7 +659,7 @@ func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...in // HTTPBodyContains asserts that a specified handler returns a // body that contains a string. // -// assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { @@ -579,7 +675,7 @@ func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method string, url s // HTTPBodyContainsf asserts that a specified handler returns a // body that contains a string. // -// assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { @@ -595,7 +691,7 @@ func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url // HTTPBodyNotContains asserts that a specified handler returns a // body that does not contain a string. // -// assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { @@ -611,7 +707,7 @@ func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method string, ur // HTTPBodyNotContainsf asserts that a specified handler returns a // body that does not contain a string. // -// assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { @@ -626,7 +722,7 @@ func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, u // HTTPError asserts that a specified handler returns an error status code. // -// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPError(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { @@ -641,7 +737,7 @@ func HTTPError(t TestingT, handler http.HandlerFunc, method string, url string, // HTTPErrorf asserts that a specified handler returns an error status code. // -// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { @@ -656,7 +752,7 @@ func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, // HTTPRedirect asserts that a specified handler returns a redirect status code. // -// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPRedirect(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { @@ -671,7 +767,7 @@ func HTTPRedirect(t TestingT, handler http.HandlerFunc, method string, url strin // HTTPRedirectf asserts that a specified handler returns a redirect status code. // -// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { @@ -686,7 +782,7 @@ func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url stri // HTTPStatusCode asserts that a specified handler returns a specified status code. // -// assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) +// assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) // // Returns whether the assertion was successful (true) or not (false). func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) { @@ -701,7 +797,7 @@ func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method string, url str // HTTPStatusCodef asserts that a specified handler returns a specified status code. // -// assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") +// assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) { @@ -716,7 +812,7 @@ func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url st // HTTPSuccess asserts that a specified handler returns a success status code. // -// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) +// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) // // Returns whether the assertion was successful (true) or not (false). func HTTPSuccess(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { @@ -731,7 +827,7 @@ func HTTPSuccess(t TestingT, handler http.HandlerFunc, method string, url string // HTTPSuccessf asserts that a specified handler returns a success status code. // -// assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") +// assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { @@ -746,7 +842,7 @@ func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url strin // Implements asserts that an object is implemented by the specified interface. // -// assert.Implements(t, (*MyInterface)(nil), new(MyObject)) +// assert.Implements(t, (*MyInterface)(nil), new(MyObject)) func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -759,7 +855,7 @@ func Implements(t TestingT, interfaceObject interface{}, object interface{}, msg // Implementsf asserts that an object is implemented by the specified interface. // -// assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") +// assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -772,7 +868,7 @@ func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, ms // InDelta asserts that the two numerals are within delta of each other. // -// assert.InDelta(t, math.Pi, 22/7.0, 0.01) +// assert.InDelta(t, math.Pi, 22/7.0, 0.01) func InDelta(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -829,7 +925,7 @@ func InDeltaSlicef(t TestingT, expected interface{}, actual interface{}, delta f // InDeltaf asserts that the two numerals are within delta of each other. // -// assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted") +// assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted") func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -886,9 +982,9 @@ func InEpsilonf(t TestingT, expected interface{}, actual interface{}, epsilon fl // IsDecreasing asserts that the collection is decreasing // -// assert.IsDecreasing(t, []int{2, 1, 0}) -// assert.IsDecreasing(t, []float{2, 1}) -// assert.IsDecreasing(t, []string{"b", "a"}) +// assert.IsDecreasing(t, []int{2, 1, 0}) +// assert.IsDecreasing(t, []float{2, 1}) +// assert.IsDecreasing(t, []string{"b", "a"}) func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -901,9 +997,9 @@ func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { // IsDecreasingf asserts that the collection is decreasing // -// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted") -// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted") -// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted") +// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted") +// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted") +// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted") func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -916,9 +1012,9 @@ func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface // IsIncreasing asserts that the collection is increasing // -// assert.IsIncreasing(t, []int{1, 2, 3}) -// assert.IsIncreasing(t, []float{1, 2}) -// assert.IsIncreasing(t, []string{"a", "b"}) +// assert.IsIncreasing(t, []int{1, 2, 3}) +// assert.IsIncreasing(t, []float{1, 2}) +// assert.IsIncreasing(t, []string{"a", "b"}) func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -931,9 +1027,9 @@ func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { // IsIncreasingf asserts that the collection is increasing // -// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted") -// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted") -// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted") +// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted") +// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted") +// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted") func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -946,9 +1042,9 @@ func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface // IsNonDecreasing asserts that the collection is not decreasing // -// assert.IsNonDecreasing(t, []int{1, 1, 2}) -// assert.IsNonDecreasing(t, []float{1, 2}) -// assert.IsNonDecreasing(t, []string{"a", "b"}) +// assert.IsNonDecreasing(t, []int{1, 1, 2}) +// assert.IsNonDecreasing(t, []float{1, 2}) +// assert.IsNonDecreasing(t, []string{"a", "b"}) func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -961,9 +1057,9 @@ func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) // IsNonDecreasingf asserts that the collection is not decreasing // -// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted") -// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted") -// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted") +// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted") +// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted") +// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted") func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -976,9 +1072,9 @@ func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interf // IsNonIncreasing asserts that the collection is not increasing // -// assert.IsNonIncreasing(t, []int{2, 1, 1}) -// assert.IsNonIncreasing(t, []float{2, 1}) -// assert.IsNonIncreasing(t, []string{"b", "a"}) +// assert.IsNonIncreasing(t, []int{2, 1, 1}) +// assert.IsNonIncreasing(t, []float{2, 1}) +// assert.IsNonIncreasing(t, []string{"b", "a"}) func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -991,9 +1087,9 @@ func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) // IsNonIncreasingf asserts that the collection is not increasing // -// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted") -// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted") -// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted") +// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted") +// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted") +// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted") func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1028,7 +1124,7 @@ func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg strin // JSONEq asserts that two JSON strings are equivalent. // -// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) +// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1041,7 +1137,7 @@ func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{ // JSONEqf asserts that two JSON strings are equivalent. // -// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") +// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1055,7 +1151,7 @@ func JSONEqf(t TestingT, expected string, actual string, msg string, args ...int // Len asserts that the specified object has specific length. // Len also fails if the object has a type that len() not accept. // -// assert.Len(t, mySlice, 3) +// assert.Len(t, mySlice, 3) func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1069,7 +1165,7 @@ func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) // Lenf asserts that the specified object has specific length. // Lenf also fails if the object has a type that len() not accept. // -// assert.Lenf(t, mySlice, 3, "error message %s", "formatted") +// assert.Lenf(t, mySlice, 3, "error message %s", "formatted") func Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1082,9 +1178,9 @@ func Lenf(t TestingT, object interface{}, length int, msg string, args ...interf // Less asserts that the first element is less than the second // -// assert.Less(t, 1, 2) -// assert.Less(t, float64(1), float64(2)) -// assert.Less(t, "a", "b") +// assert.Less(t, 1, 2) +// assert.Less(t, float64(1), float64(2)) +// assert.Less(t, "a", "b") func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1097,10 +1193,10 @@ func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) // LessOrEqual asserts that the first element is less than or equal to the second // -// assert.LessOrEqual(t, 1, 2) -// assert.LessOrEqual(t, 2, 2) -// assert.LessOrEqual(t, "a", "b") -// assert.LessOrEqual(t, "b", "b") +// assert.LessOrEqual(t, 1, 2) +// assert.LessOrEqual(t, 2, 2) +// assert.LessOrEqual(t, "a", "b") +// assert.LessOrEqual(t, "b", "b") func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1113,10 +1209,10 @@ func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...inter // LessOrEqualf asserts that the first element is less than or equal to the second // -// assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted") -// assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted") -// assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted") -// assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted") +// assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted") +// assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted") +// assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted") +// assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted") func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1129,9 +1225,9 @@ func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args . // Lessf asserts that the first element is less than the second // -// assert.Lessf(t, 1, 2, "error message %s", "formatted") -// assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted") -// assert.Lessf(t, "a", "b", "error message %s", "formatted") +// assert.Lessf(t, 1, 2, "error message %s", "formatted") +// assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted") +// assert.Lessf(t, "a", "b", "error message %s", "formatted") func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1144,8 +1240,8 @@ func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...inter // Negative asserts that the specified element is negative // -// assert.Negative(t, -1) -// assert.Negative(t, -1.23) +// assert.Negative(t, -1) +// assert.Negative(t, -1.23) func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1158,8 +1254,8 @@ func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) { // Negativef asserts that the specified element is negative // -// assert.Negativef(t, -1, "error message %s", "formatted") -// assert.Negativef(t, -1.23, "error message %s", "formatted") +// assert.Negativef(t, -1, "error message %s", "formatted") +// assert.Negativef(t, -1.23, "error message %s", "formatted") func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1173,7 +1269,7 @@ func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) { // Never asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) +// assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1187,7 +1283,7 @@ func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.D // Neverf asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1200,7 +1296,7 @@ func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time. // Nil asserts that the specified object is nil. // -// assert.Nil(t, err) +// assert.Nil(t, err) func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1213,7 +1309,7 @@ func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) { // Nilf asserts that the specified object is nil. // -// assert.Nilf(t, err, "error message %s", "formatted") +// assert.Nilf(t, err, "error message %s", "formatted") func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1250,10 +1346,10 @@ func NoDirExistsf(t TestingT, path string, msg string, args ...interface{}) { // NoError asserts that a function returned no error (i.e. `nil`). // -// actualObj, err := SomeFunction() -// if assert.NoError(t, err) { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// if assert.NoError(t, err) { +// assert.Equal(t, expectedObj, actualObj) +// } func NoError(t TestingT, err error, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1266,10 +1362,10 @@ func NoError(t TestingT, err error, msgAndArgs ...interface{}) { // NoErrorf asserts that a function returned no error (i.e. `nil`). // -// actualObj, err := SomeFunction() -// if assert.NoErrorf(t, err, "error message %s", "formatted") { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// if assert.NoErrorf(t, err, "error message %s", "formatted") { +// assert.Equal(t, expectedObj, actualObj) +// } func NoErrorf(t TestingT, err error, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1307,9 +1403,9 @@ func NoFileExistsf(t TestingT, path string, msg string, args ...interface{}) { // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// assert.NotContains(t, "Hello World", "Earth") -// assert.NotContains(t, ["Hello", "World"], "Earth") -// assert.NotContains(t, {"Hello": "World"}, "Earth") +// assert.NotContains(t, "Hello World", "Earth") +// assert.NotContains(t, ["Hello", "World"], "Earth") +// assert.NotContains(t, {"Hello": "World"}, "Earth") func NotContains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1323,9 +1419,9 @@ func NotContains(t TestingT, s interface{}, contains interface{}, msgAndArgs ... // NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted") -// assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted") -// assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted") +// assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted") +// assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted") +// assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted") func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1339,9 +1435,9 @@ func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, a // NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // -// if assert.NotEmpty(t, obj) { -// assert.Equal(t, "two", obj[1]) -// } +// if assert.NotEmpty(t, obj) { +// assert.Equal(t, "two", obj[1]) +// } func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1355,9 +1451,9 @@ func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) { // NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // -// if assert.NotEmptyf(t, obj, "error message %s", "formatted") { -// assert.Equal(t, "two", obj[1]) -// } +// if assert.NotEmptyf(t, obj, "error message %s", "formatted") { +// assert.Equal(t, "two", obj[1]) +// } func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1370,7 +1466,7 @@ func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) // NotEqual asserts that the specified values are NOT equal. // -// assert.NotEqual(t, obj1, obj2) +// assert.NotEqual(t, obj1, obj2) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -1386,7 +1482,7 @@ func NotEqual(t TestingT, expected interface{}, actual interface{}, msgAndArgs . // NotEqualValues asserts that two objects are not equal even when converted to the same type // -// assert.NotEqualValues(t, obj1, obj2) +// assert.NotEqualValues(t, obj1, obj2) func NotEqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1399,7 +1495,7 @@ func NotEqualValues(t TestingT, expected interface{}, actual interface{}, msgAnd // NotEqualValuesf asserts that two objects are not equal even when converted to the same type // -// assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted") +// assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted") func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1412,7 +1508,7 @@ func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg s // NotEqualf asserts that the specified values are NOT equal. // -// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted") +// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -1452,7 +1548,7 @@ func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interf // NotNil asserts that the specified object is not nil. // -// assert.NotNil(t, err) +// assert.NotNil(t, err) func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1465,7 +1561,7 @@ func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) { // NotNilf asserts that the specified object is not nil. // -// assert.NotNilf(t, err, "error message %s", "formatted") +// assert.NotNilf(t, err, "error message %s", "formatted") func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1478,7 +1574,7 @@ func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) { // NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. // -// assert.NotPanics(t, func(){ RemainCalm() }) +// assert.NotPanics(t, func(){ RemainCalm() }) func NotPanics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1491,7 +1587,7 @@ func NotPanics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { // NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. // -// assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted") +// assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted") func NotPanicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1504,8 +1600,8 @@ func NotPanicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interfac // NotRegexp asserts that a specified regexp does not match a string. // -// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") -// assert.NotRegexp(t, "^start", "it's not starting") +// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") +// assert.NotRegexp(t, "^start", "it's not starting") func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1518,8 +1614,8 @@ func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interf // NotRegexpf asserts that a specified regexp does not match a string. // -// assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") -// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") +// assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") +// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1532,7 +1628,7 @@ func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args .. // NotSame asserts that two pointers do not reference the same object. // -// assert.NotSame(t, ptr1, ptr2) +// assert.NotSame(t, ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1548,7 +1644,7 @@ func NotSame(t TestingT, expected interface{}, actual interface{}, msgAndArgs .. // NotSamef asserts that two pointers do not reference the same object. // -// assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted") +// assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1565,7 +1661,7 @@ func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string, // NotSubset asserts that the specified list(array, slice...) contains not all // elements given in the specified subset(array, slice...). // -// assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") +// assert.NotSubset(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") func NotSubset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1579,7 +1675,7 @@ func NotSubset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...i // NotSubsetf asserts that the specified list(array, slice...) contains not all // elements given in the specified subset(array, slice...). // -// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") +// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1614,7 +1710,7 @@ func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) { // Panics asserts that the code inside the specified PanicTestFunc panics. // -// assert.Panics(t, func(){ GoCrazy() }) +// assert.Panics(t, func(){ GoCrazy() }) func Panics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1629,7 +1725,7 @@ func Panics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) +// assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) func PanicsWithError(t TestingT, errString string, f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1644,7 +1740,7 @@ func PanicsWithError(t TestingT, errString string, f assert.PanicTestFunc, msgAn // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func PanicsWithErrorf(t TestingT, errString string, f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1658,7 +1754,7 @@ func PanicsWithErrorf(t TestingT, errString string, f assert.PanicTestFunc, msg // PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() }) +// assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() }) func PanicsWithValue(t TestingT, expected interface{}, f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1672,7 +1768,7 @@ func PanicsWithValue(t TestingT, expected interface{}, f assert.PanicTestFunc, m // PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func PanicsWithValuef(t TestingT, expected interface{}, f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1685,7 +1781,7 @@ func PanicsWithValuef(t TestingT, expected interface{}, f assert.PanicTestFunc, // Panicsf asserts that the code inside the specified PanicTestFunc panics. // -// assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted") +// assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted") func Panicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1698,8 +1794,8 @@ func Panicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{} // Positive asserts that the specified element is positive // -// assert.Positive(t, 1) -// assert.Positive(t, 1.23) +// assert.Positive(t, 1) +// assert.Positive(t, 1.23) func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1712,8 +1808,8 @@ func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) { // Positivef asserts that the specified element is positive // -// assert.Positivef(t, 1, "error message %s", "formatted") -// assert.Positivef(t, 1.23, "error message %s", "formatted") +// assert.Positivef(t, 1, "error message %s", "formatted") +// assert.Positivef(t, 1.23, "error message %s", "formatted") func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1726,8 +1822,8 @@ func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) { // Regexp asserts that a specified regexp matches a string. // -// assert.Regexp(t, regexp.MustCompile("start"), "it's starting") -// assert.Regexp(t, "start...$", "it's not starting") +// assert.Regexp(t, regexp.MustCompile("start"), "it's starting") +// assert.Regexp(t, "start...$", "it's not starting") func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1740,8 +1836,8 @@ func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface // Regexpf asserts that a specified regexp matches a string. // -// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") -// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") +// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") +// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1754,7 +1850,7 @@ func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...in // Same asserts that two pointers reference the same object. // -// assert.Same(t, ptr1, ptr2) +// assert.Same(t, ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1770,7 +1866,7 @@ func Same(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...in // Samef asserts that two pointers reference the same object. // -// assert.Samef(t, ptr1, ptr2, "error message %s", "formatted") +// assert.Samef(t, ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1787,7 +1883,7 @@ func Samef(t TestingT, expected interface{}, actual interface{}, msg string, arg // Subset asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // -// assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") +// assert.Subset(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") func Subset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1801,7 +1897,7 @@ func Subset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...inte // Subsetf asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // -// assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") +// assert.Subsetf(t, [1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1814,7 +1910,7 @@ func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args // True asserts that the specified value is true. // -// assert.True(t, myBool) +// assert.True(t, myBool) func True(t TestingT, value bool, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1827,7 +1923,7 @@ func True(t TestingT, value bool, msgAndArgs ...interface{}) { // Truef asserts that the specified value is true. // -// assert.Truef(t, myBool, "error message %s", "formatted") +// assert.Truef(t, myBool, "error message %s", "formatted") func Truef(t TestingT, value bool, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1840,7 +1936,7 @@ func Truef(t TestingT, value bool, msg string, args ...interface{}) { // WithinDuration asserts that the two times are within duration delta of each other. // -// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second) +// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second) func WithinDuration(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1853,7 +1949,7 @@ func WithinDuration(t TestingT, expected time.Time, actual time.Time, delta time // WithinDurationf asserts that the two times are within duration delta of each other. // -// assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") +// assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1866,7 +1962,7 @@ func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta tim // WithinRange asserts that a time is within a time range (inclusive). // -// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) func WithinRange(t TestingT, actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() @@ -1879,7 +1975,7 @@ func WithinRange(t TestingT, actual time.Time, start time.Time, end time.Time, m // WithinRangef asserts that a time is within a time range (inclusive). // -// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/stretchr/testify/require/require_forward.go b/vendor/github.com/stretchr/testify/require/require_forward.go index 960bf6f2..3b5b0933 100644 --- a/vendor/github.com/stretchr/testify/require/require_forward.go +++ b/vendor/github.com/stretchr/testify/require/require_forward.go @@ -31,9 +31,9 @@ func (a *Assertions) Conditionf(comp assert.Comparison, msg string, args ...inte // Contains asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// a.Contains("Hello World", "World") -// a.Contains(["Hello", "World"], "World") -// a.Contains({"Hello": "World"}, "Hello") +// a.Contains("Hello World", "World") +// a.Contains(["Hello", "World"], "World") +// a.Contains({"Hello": "World"}, "Hello") func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -44,9 +44,9 @@ func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs .. // Containsf asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // -// a.Containsf("Hello World", "World", "error message %s", "formatted") -// a.Containsf(["Hello", "World"], "World", "error message %s", "formatted") -// a.Containsf({"Hello": "World"}, "Hello", "error message %s", "formatted") +// a.Containsf("Hello World", "World", "error message %s", "formatted") +// a.Containsf(["Hello", "World"], "World", "error message %s", "formatted") +// a.Containsf({"Hello": "World"}, "Hello", "error message %s", "formatted") func (a *Assertions) Containsf(s interface{}, contains interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -99,7 +99,7 @@ func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg st // Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // -// a.Empty(obj) +// a.Empty(obj) func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -110,7 +110,7 @@ func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) { // Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either // a slice or a channel with len == 0. // -// a.Emptyf(obj, "error message %s", "formatted") +// a.Emptyf(obj, "error message %s", "formatted") func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -120,7 +120,7 @@ func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) // Equal asserts that two objects are equal. // -// a.Equal(123, 123) +// a.Equal(123, 123) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -135,8 +135,8 @@ func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs // EqualError asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // -// actualObj, err := SomeFunction() -// a.EqualError(err, expectedErrorString) +// actualObj, err := SomeFunction() +// a.EqualError(err, expectedErrorString) func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -147,8 +147,8 @@ func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ... // EqualErrorf asserts that a function returned an error (i.e. not `nil`) // and that it is equal to the provided error. // -// actualObj, err := SomeFunction() -// a.EqualErrorf(err, expectedErrorString, "error message %s", "formatted") +// actualObj, err := SomeFunction() +// a.EqualErrorf(err, expectedErrorString, "error message %s", "formatted") func (a *Assertions) EqualErrorf(theError error, errString string, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -156,10 +156,44 @@ func (a *Assertions) EqualErrorf(theError error, errString string, msg string, a EqualErrorf(a.t, theError, errString, msg, args...) } +// EqualExportedValues asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// a.EqualExportedValues(S{1, 2}, S{1, 3}) => true +// a.EqualExportedValues(S{1, 2}, S{2, 3}) => false +func (a *Assertions) EqualExportedValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + EqualExportedValues(a.t, expected, actual, msgAndArgs...) +} + +// EqualExportedValuesf asserts that the types of two objects are equal and their public +// fields are also equal. This is useful for comparing structs that have private fields +// that could potentially differ. +// +// type S struct { +// Exported int +// notExported int +// } +// a.EqualExportedValuesf(S{1, 2}, S{1, 3}, "error message %s", "formatted") => true +// a.EqualExportedValuesf(S{1, 2}, S{2, 3}, "error message %s", "formatted") => false +func (a *Assertions) EqualExportedValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + EqualExportedValuesf(a.t, expected, actual, msg, args...) +} + // EqualValues asserts that two objects are equal or convertable to the same types // and equal. // -// a.EqualValues(uint32(123), int32(123)) +// a.EqualValues(uint32(123), int32(123)) func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -170,7 +204,7 @@ func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAn // EqualValuesf asserts that two objects are equal or convertable to the same types // and equal. // -// a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted") +// a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted") func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -180,7 +214,7 @@ func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg // Equalf asserts that two objects are equal. // -// a.Equalf(123, 123, "error message %s", "formatted") +// a.Equalf(123, 123, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). Function equality @@ -194,10 +228,10 @@ func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string // Error asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if a.Error(err) { -// assert.Equal(t, expectedError, err) -// } +// actualObj, err := SomeFunction() +// if a.Error(err) { +// assert.Equal(t, expectedError, err) +// } func (a *Assertions) Error(err error, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -226,8 +260,8 @@ func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args .. // ErrorContains asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // -// actualObj, err := SomeFunction() -// a.ErrorContains(err, expectedErrorSubString) +// actualObj, err := SomeFunction() +// a.ErrorContains(err, expectedErrorSubString) func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -238,8 +272,8 @@ func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs . // ErrorContainsf asserts that a function returned an error (i.e. not `nil`) // and that the error contains the specified substring. // -// actualObj, err := SomeFunction() -// a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted") +// actualObj, err := SomeFunction() +// a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted") func (a *Assertions) ErrorContainsf(theError error, contains string, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -267,10 +301,10 @@ func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...inter // Errorf asserts that a function returned an error (i.e. not `nil`). // -// actualObj, err := SomeFunction() -// if a.Errorf(err, "error message %s", "formatted") { -// assert.Equal(t, expectedErrorf, err) -// } +// actualObj, err := SomeFunction() +// if a.Errorf(err, "error message %s", "formatted") { +// assert.Equal(t, expectedErrorf, err) +// } func (a *Assertions) Errorf(err error, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -281,7 +315,7 @@ func (a *Assertions) Errorf(err error, msg string, args ...interface{}) { // Eventually asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// a.Eventually(func() bool { return true; }, time.Second, 10*time.Millisecond) +// a.Eventually(func() bool { return true; }, time.Second, 10*time.Millisecond) func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -289,10 +323,60 @@ func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, ti Eventually(a.t, condition, waitFor, tick, msgAndArgs...) } +// EventuallyWithT asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// a.EventuallyWithT(func(c *assert.CollectT) { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func (a *Assertions) EventuallyWithT(condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + EventuallyWithT(a.t, condition, waitFor, tick, msgAndArgs...) +} + +// EventuallyWithTf asserts that given condition will be met in waitFor time, +// periodically checking target function each tick. In contrast to Eventually, +// it supplies a CollectT to the condition function, so that the condition +// function can use the CollectT to call other assertions. +// The condition is considered "met" if no errors are raised in a tick. +// The supplied CollectT collects all errors from one tick (if there are any). +// If the condition is not met before waitFor, the collected errors of +// the last tick are copied to t. +// +// externalValue := false +// go func() { +// time.Sleep(8*time.Second) +// externalValue = true +// }() +// a.EventuallyWithTf(func(c *assert.CollectT, "error message %s", "formatted") { +// // add assertions as needed; any assertion failure will fail the current tick +// assert.True(c, externalValue, "expected 'externalValue' to be true") +// }, 1*time.Second, 10*time.Second, "external state has not changed to 'true'; still false") +func (a *Assertions) EventuallyWithTf(condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { + if h, ok := a.t.(tHelper); ok { + h.Helper() + } + EventuallyWithTf(a.t, condition, waitFor, tick, msg, args...) +} + // Eventuallyf asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // -// a.Eventuallyf(func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// a.Eventuallyf(func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func (a *Assertions) Eventuallyf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -302,7 +386,7 @@ func (a *Assertions) Eventuallyf(condition func() bool, waitFor time.Duration, t // Exactly asserts that two objects are equal in value and type. // -// a.Exactly(int32(123), int64(123)) +// a.Exactly(int32(123), int64(123)) func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -312,7 +396,7 @@ func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArg // Exactlyf asserts that two objects are equal in value and type. // -// a.Exactlyf(int32(123), int64(123), "error message %s", "formatted") +// a.Exactlyf(int32(123), int64(123), "error message %s", "formatted") func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -354,7 +438,7 @@ func (a *Assertions) Failf(failureMessage string, msg string, args ...interface{ // False asserts that the specified value is false. // -// a.False(myBool) +// a.False(myBool) func (a *Assertions) False(value bool, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -364,7 +448,7 @@ func (a *Assertions) False(value bool, msgAndArgs ...interface{}) { // Falsef asserts that the specified value is false. // -// a.Falsef(myBool, "error message %s", "formatted") +// a.Falsef(myBool, "error message %s", "formatted") func (a *Assertions) Falsef(value bool, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -392,9 +476,9 @@ func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) { // Greater asserts that the first element is greater than the second // -// a.Greater(2, 1) -// a.Greater(float64(2), float64(1)) -// a.Greater("b", "a") +// a.Greater(2, 1) +// a.Greater(float64(2), float64(1)) +// a.Greater("b", "a") func (a *Assertions) Greater(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -404,10 +488,10 @@ func (a *Assertions) Greater(e1 interface{}, e2 interface{}, msgAndArgs ...inter // GreaterOrEqual asserts that the first element is greater than or equal to the second // -// a.GreaterOrEqual(2, 1) -// a.GreaterOrEqual(2, 2) -// a.GreaterOrEqual("b", "a") -// a.GreaterOrEqual("b", "b") +// a.GreaterOrEqual(2, 1) +// a.GreaterOrEqual(2, 2) +// a.GreaterOrEqual("b", "a") +// a.GreaterOrEqual("b", "b") func (a *Assertions) GreaterOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -417,10 +501,10 @@ func (a *Assertions) GreaterOrEqual(e1 interface{}, e2 interface{}, msgAndArgs . // GreaterOrEqualf asserts that the first element is greater than or equal to the second // -// a.GreaterOrEqualf(2, 1, "error message %s", "formatted") -// a.GreaterOrEqualf(2, 2, "error message %s", "formatted") -// a.GreaterOrEqualf("b", "a", "error message %s", "formatted") -// a.GreaterOrEqualf("b", "b", "error message %s", "formatted") +// a.GreaterOrEqualf(2, 1, "error message %s", "formatted") +// a.GreaterOrEqualf(2, 2, "error message %s", "formatted") +// a.GreaterOrEqualf("b", "a", "error message %s", "formatted") +// a.GreaterOrEqualf("b", "b", "error message %s", "formatted") func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -430,9 +514,9 @@ func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string, // Greaterf asserts that the first element is greater than the second // -// a.Greaterf(2, 1, "error message %s", "formatted") -// a.Greaterf(float64(2), float64(1), "error message %s", "formatted") -// a.Greaterf("b", "a", "error message %s", "formatted") +// a.Greaterf(2, 1, "error message %s", "formatted") +// a.Greaterf(float64(2), float64(1), "error message %s", "formatted") +// a.Greaterf("b", "a", "error message %s", "formatted") func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -443,7 +527,7 @@ func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args . // HTTPBodyContains asserts that a specified handler returns a // body that contains a string. // -// a.HTTPBodyContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// a.HTTPBodyContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { @@ -456,7 +540,7 @@ func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, u // HTTPBodyContainsf asserts that a specified handler returns a // body that contains a string. // -// a.HTTPBodyContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// a.HTTPBodyContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { @@ -469,7 +553,7 @@ func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, // HTTPBodyNotContains asserts that a specified handler returns a // body that does not contain a string. // -// a.HTTPBodyNotContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") +// a.HTTPBodyNotContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { @@ -482,7 +566,7 @@ func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string // HTTPBodyNotContainsf asserts that a specified handler returns a // body that does not contain a string. // -// a.HTTPBodyNotContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") +// a.HTTPBodyNotContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { @@ -494,7 +578,7 @@ func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method strin // HTTPError asserts that a specified handler returns an error status code. // -// a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { @@ -506,7 +590,7 @@ func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url stri // HTTPErrorf asserts that a specified handler returns an error status code. // -// a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { @@ -518,7 +602,7 @@ func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url str // HTTPRedirect asserts that a specified handler returns a redirect status code. // -// a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { @@ -530,7 +614,7 @@ func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url s // HTTPRedirectf asserts that a specified handler returns a redirect status code. // -// a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} +// a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { @@ -542,7 +626,7 @@ func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url // HTTPStatusCode asserts that a specified handler returns a specified status code. // -// a.HTTPStatusCode(myHandler, "GET", "/notImplemented", nil, 501) +// a.HTTPStatusCode(myHandler, "GET", "/notImplemented", nil, 501) // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) { @@ -554,7 +638,7 @@ func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url // HTTPStatusCodef asserts that a specified handler returns a specified status code. // -// a.HTTPStatusCodef(myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") +// a.HTTPStatusCodef(myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) { @@ -566,7 +650,7 @@ func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, ur // HTTPSuccess asserts that a specified handler returns a success status code. // -// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) +// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { @@ -578,7 +662,7 @@ func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url st // HTTPSuccessf asserts that a specified handler returns a success status code. // -// a.HTTPSuccessf(myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") +// a.HTTPSuccessf(myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { @@ -590,7 +674,7 @@ func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url s // Implements asserts that an object is implemented by the specified interface. // -// a.Implements((*MyInterface)(nil), new(MyObject)) +// a.Implements((*MyInterface)(nil), new(MyObject)) func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -600,7 +684,7 @@ func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, // Implementsf asserts that an object is implemented by the specified interface. // -// a.Implementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted") +// a.Implementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted") func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -610,7 +694,7 @@ func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{} // InDelta asserts that the two numerals are within delta of each other. // -// a.InDelta(math.Pi, 22/7.0, 0.01) +// a.InDelta(math.Pi, 22/7.0, 0.01) func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -652,7 +736,7 @@ func (a *Assertions) InDeltaSlicef(expected interface{}, actual interface{}, del // InDeltaf asserts that the two numerals are within delta of each other. // -// a.InDeltaf(math.Pi, 22/7.0, 0.01, "error message %s", "formatted") +// a.InDeltaf(math.Pi, 22/7.0, 0.01, "error message %s", "formatted") func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -694,9 +778,9 @@ func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilo // IsDecreasing asserts that the collection is decreasing // -// a.IsDecreasing([]int{2, 1, 0}) -// a.IsDecreasing([]float{2, 1}) -// a.IsDecreasing([]string{"b", "a"}) +// a.IsDecreasing([]int{2, 1, 0}) +// a.IsDecreasing([]float{2, 1}) +// a.IsDecreasing([]string{"b", "a"}) func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -706,9 +790,9 @@ func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) // IsDecreasingf asserts that the collection is decreasing // -// a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted") -// a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted") -// a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted") +// a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted") +// a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted") +// a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted") func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -718,9 +802,9 @@ func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...inter // IsIncreasing asserts that the collection is increasing // -// a.IsIncreasing([]int{1, 2, 3}) -// a.IsIncreasing([]float{1, 2}) -// a.IsIncreasing([]string{"a", "b"}) +// a.IsIncreasing([]int{1, 2, 3}) +// a.IsIncreasing([]float{1, 2}) +// a.IsIncreasing([]string{"a", "b"}) func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -730,9 +814,9 @@ func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) // IsIncreasingf asserts that the collection is increasing // -// a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted") -// a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted") -// a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted") +// a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted") +// a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted") +// a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted") func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -742,9 +826,9 @@ func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...inter // IsNonDecreasing asserts that the collection is not decreasing // -// a.IsNonDecreasing([]int{1, 1, 2}) -// a.IsNonDecreasing([]float{1, 2}) -// a.IsNonDecreasing([]string{"a", "b"}) +// a.IsNonDecreasing([]int{1, 1, 2}) +// a.IsNonDecreasing([]float{1, 2}) +// a.IsNonDecreasing([]string{"a", "b"}) func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -754,9 +838,9 @@ func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface // IsNonDecreasingf asserts that the collection is not decreasing // -// a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted") -// a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted") -// a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted") +// a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted") +// a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted") +// a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted") func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -766,9 +850,9 @@ func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...in // IsNonIncreasing asserts that the collection is not increasing // -// a.IsNonIncreasing([]int{2, 1, 1}) -// a.IsNonIncreasing([]float{2, 1}) -// a.IsNonIncreasing([]string{"b", "a"}) +// a.IsNonIncreasing([]int{2, 1, 1}) +// a.IsNonIncreasing([]float{2, 1}) +// a.IsNonIncreasing([]string{"b", "a"}) func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -778,9 +862,9 @@ func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface // IsNonIncreasingf asserts that the collection is not increasing // -// a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted") -// a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted") -// a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted") +// a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted") +// a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted") +// a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted") func (a *Assertions) IsNonIncreasingf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -806,7 +890,7 @@ func (a *Assertions) IsTypef(expectedType interface{}, object interface{}, msg s // JSONEq asserts that two JSON strings are equivalent. // -// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) +// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -816,7 +900,7 @@ func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interf // JSONEqf asserts that two JSON strings are equivalent. // -// a.JSONEqf(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") +// a.JSONEqf(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") func (a *Assertions) JSONEqf(expected string, actual string, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -827,7 +911,7 @@ func (a *Assertions) JSONEqf(expected string, actual string, msg string, args .. // Len asserts that the specified object has specific length. // Len also fails if the object has a type that len() not accept. // -// a.Len(mySlice, 3) +// a.Len(mySlice, 3) func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -838,7 +922,7 @@ func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface // Lenf asserts that the specified object has specific length. // Lenf also fails if the object has a type that len() not accept. // -// a.Lenf(mySlice, 3, "error message %s", "formatted") +// a.Lenf(mySlice, 3, "error message %s", "formatted") func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -848,9 +932,9 @@ func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...in // Less asserts that the first element is less than the second // -// a.Less(1, 2) -// a.Less(float64(1), float64(2)) -// a.Less("a", "b") +// a.Less(1, 2) +// a.Less(float64(1), float64(2)) +// a.Less("a", "b") func (a *Assertions) Less(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -860,10 +944,10 @@ func (a *Assertions) Less(e1 interface{}, e2 interface{}, msgAndArgs ...interfac // LessOrEqual asserts that the first element is less than or equal to the second // -// a.LessOrEqual(1, 2) -// a.LessOrEqual(2, 2) -// a.LessOrEqual("a", "b") -// a.LessOrEqual("b", "b") +// a.LessOrEqual(1, 2) +// a.LessOrEqual(2, 2) +// a.LessOrEqual("a", "b") +// a.LessOrEqual("b", "b") func (a *Assertions) LessOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -873,10 +957,10 @@ func (a *Assertions) LessOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...i // LessOrEqualf asserts that the first element is less than or equal to the second // -// a.LessOrEqualf(1, 2, "error message %s", "formatted") -// a.LessOrEqualf(2, 2, "error message %s", "formatted") -// a.LessOrEqualf("a", "b", "error message %s", "formatted") -// a.LessOrEqualf("b", "b", "error message %s", "formatted") +// a.LessOrEqualf(1, 2, "error message %s", "formatted") +// a.LessOrEqualf(2, 2, "error message %s", "formatted") +// a.LessOrEqualf("a", "b", "error message %s", "formatted") +// a.LessOrEqualf("b", "b", "error message %s", "formatted") func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -886,9 +970,9 @@ func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, ar // Lessf asserts that the first element is less than the second // -// a.Lessf(1, 2, "error message %s", "formatted") -// a.Lessf(float64(1), float64(2), "error message %s", "formatted") -// a.Lessf("a", "b", "error message %s", "formatted") +// a.Lessf(1, 2, "error message %s", "formatted") +// a.Lessf(float64(1), float64(2), "error message %s", "formatted") +// a.Lessf("a", "b", "error message %s", "formatted") func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -898,8 +982,8 @@ func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...i // Negative asserts that the specified element is negative // -// a.Negative(-1) -// a.Negative(-1.23) +// a.Negative(-1) +// a.Negative(-1.23) func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -909,8 +993,8 @@ func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) { // Negativef asserts that the specified element is negative // -// a.Negativef(-1, "error message %s", "formatted") -// a.Negativef(-1.23, "error message %s", "formatted") +// a.Negativef(-1, "error message %s", "formatted") +// a.Negativef(-1.23, "error message %s", "formatted") func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -921,7 +1005,7 @@ func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) { // Never asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// a.Never(func() bool { return false; }, time.Second, 10*time.Millisecond) +// a.Never(func() bool { return false; }, time.Second, 10*time.Millisecond) func (a *Assertions) Never(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -932,7 +1016,7 @@ func (a *Assertions) Never(condition func() bool, waitFor time.Duration, tick ti // Neverf asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // -// a.Neverf(func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") +// a.Neverf(func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") func (a *Assertions) Neverf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -942,7 +1026,7 @@ func (a *Assertions) Neverf(condition func() bool, waitFor time.Duration, tick t // Nil asserts that the specified object is nil. // -// a.Nil(err) +// a.Nil(err) func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -952,7 +1036,7 @@ func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) { // Nilf asserts that the specified object is nil. // -// a.Nilf(err, "error message %s", "formatted") +// a.Nilf(err, "error message %s", "formatted") func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -980,10 +1064,10 @@ func (a *Assertions) NoDirExistsf(path string, msg string, args ...interface{}) // NoError asserts that a function returned no error (i.e. `nil`). // -// actualObj, err := SomeFunction() -// if a.NoError(err) { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// if a.NoError(err) { +// assert.Equal(t, expectedObj, actualObj) +// } func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -993,10 +1077,10 @@ func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) { // NoErrorf asserts that a function returned no error (i.e. `nil`). // -// actualObj, err := SomeFunction() -// if a.NoErrorf(err, "error message %s", "formatted") { -// assert.Equal(t, expectedObj, actualObj) -// } +// actualObj, err := SomeFunction() +// if a.NoErrorf(err, "error message %s", "formatted") { +// assert.Equal(t, expectedObj, actualObj) +// } func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1025,9 +1109,9 @@ func (a *Assertions) NoFileExistsf(path string, msg string, args ...interface{}) // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// a.NotContains("Hello World", "Earth") -// a.NotContains(["Hello", "World"], "Earth") -// a.NotContains({"Hello": "World"}, "Earth") +// a.NotContains("Hello World", "Earth") +// a.NotContains(["Hello", "World"], "Earth") +// a.NotContains({"Hello": "World"}, "Earth") func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1038,9 +1122,9 @@ func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs // NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // -// a.NotContainsf("Hello World", "Earth", "error message %s", "formatted") -// a.NotContainsf(["Hello", "World"], "Earth", "error message %s", "formatted") -// a.NotContainsf({"Hello": "World"}, "Earth", "error message %s", "formatted") +// a.NotContainsf("Hello World", "Earth", "error message %s", "formatted") +// a.NotContainsf(["Hello", "World"], "Earth", "error message %s", "formatted") +// a.NotContainsf({"Hello": "World"}, "Earth", "error message %s", "formatted") func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1051,9 +1135,9 @@ func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg strin // NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // -// if a.NotEmpty(obj) { -// assert.Equal(t, "two", obj[1]) -// } +// if a.NotEmpty(obj) { +// assert.Equal(t, "two", obj[1]) +// } func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1064,9 +1148,9 @@ func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) { // NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either // a slice or a channel with len == 0. // -// if a.NotEmptyf(obj, "error message %s", "formatted") { -// assert.Equal(t, "two", obj[1]) -// } +// if a.NotEmptyf(obj, "error message %s", "formatted") { +// assert.Equal(t, "two", obj[1]) +// } func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1076,7 +1160,7 @@ func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface // NotEqual asserts that the specified values are NOT equal. // -// a.NotEqual(obj1, obj2) +// a.NotEqual(obj1, obj2) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -1089,7 +1173,7 @@ func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndAr // NotEqualValues asserts that two objects are not equal even when converted to the same type // -// a.NotEqualValues(obj1, obj2) +// a.NotEqualValues(obj1, obj2) func (a *Assertions) NotEqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1099,7 +1183,7 @@ func (a *Assertions) NotEqualValues(expected interface{}, actual interface{}, ms // NotEqualValuesf asserts that two objects are not equal even when converted to the same type // -// a.NotEqualValuesf(obj1, obj2, "error message %s", "formatted") +// a.NotEqualValuesf(obj1, obj2, "error message %s", "formatted") func (a *Assertions) NotEqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1109,7 +1193,7 @@ func (a *Assertions) NotEqualValuesf(expected interface{}, actual interface{}, m // NotEqualf asserts that the specified values are NOT equal. // -// a.NotEqualf(obj1, obj2, "error message %s", "formatted") +// a.NotEqualf(obj1, obj2, "error message %s", "formatted") // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). @@ -1140,7 +1224,7 @@ func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...in // NotNil asserts that the specified object is not nil. // -// a.NotNil(err) +// a.NotNil(err) func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1150,7 +1234,7 @@ func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) { // NotNilf asserts that the specified object is not nil. // -// a.NotNilf(err, "error message %s", "formatted") +// a.NotNilf(err, "error message %s", "formatted") func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1160,7 +1244,7 @@ func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{} // NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. // -// a.NotPanics(func(){ RemainCalm() }) +// a.NotPanics(func(){ RemainCalm() }) func (a *Assertions) NotPanics(f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1170,7 +1254,7 @@ func (a *Assertions) NotPanics(f assert.PanicTestFunc, msgAndArgs ...interface{} // NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. // -// a.NotPanicsf(func(){ RemainCalm() }, "error message %s", "formatted") +// a.NotPanicsf(func(){ RemainCalm() }, "error message %s", "formatted") func (a *Assertions) NotPanicsf(f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1180,8 +1264,8 @@ func (a *Assertions) NotPanicsf(f assert.PanicTestFunc, msg string, args ...inte // NotRegexp asserts that a specified regexp does not match a string. // -// a.NotRegexp(regexp.MustCompile("starts"), "it's starting") -// a.NotRegexp("^start", "it's not starting") +// a.NotRegexp(regexp.MustCompile("starts"), "it's starting") +// a.NotRegexp("^start", "it's not starting") func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1191,8 +1275,8 @@ func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...in // NotRegexpf asserts that a specified regexp does not match a string. // -// a.NotRegexpf(regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") -// a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") +// a.NotRegexpf(regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") +// a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1202,7 +1286,7 @@ func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, arg // NotSame asserts that two pointers do not reference the same object. // -// a.NotSame(ptr1, ptr2) +// a.NotSame(ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1215,7 +1299,7 @@ func (a *Assertions) NotSame(expected interface{}, actual interface{}, msgAndArg // NotSamef asserts that two pointers do not reference the same object. // -// a.NotSamef(ptr1, ptr2, "error message %s", "formatted") +// a.NotSamef(ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1229,7 +1313,7 @@ func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg stri // NotSubset asserts that the specified list(array, slice...) contains not all // elements given in the specified subset(array, slice...). // -// a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") +// a.NotSubset([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]") func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1240,7 +1324,7 @@ func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs // NotSubsetf asserts that the specified list(array, slice...) contains not all // elements given in the specified subset(array, slice...). // -// a.NotSubsetf([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") +// a.NotSubsetf([1, 3, 4], [1, 2], "But [1, 3, 4] does not contain [1, 2]", "error message %s", "formatted") func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1266,7 +1350,7 @@ func (a *Assertions) NotZerof(i interface{}, msg string, args ...interface{}) { // Panics asserts that the code inside the specified PanicTestFunc panics. // -// a.Panics(func(){ GoCrazy() }) +// a.Panics(func(){ GoCrazy() }) func (a *Assertions) Panics(f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1278,7 +1362,7 @@ func (a *Assertions) Panics(f assert.PanicTestFunc, msgAndArgs ...interface{}) { // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// a.PanicsWithError("crazy error", func(){ GoCrazy() }) +// a.PanicsWithError("crazy error", func(){ GoCrazy() }) func (a *Assertions) PanicsWithError(errString string, f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1290,7 +1374,7 @@ func (a *Assertions) PanicsWithError(errString string, f assert.PanicTestFunc, m // panics, and that the recovered panic value is an error that satisfies the // EqualError comparison. // -// a.PanicsWithErrorf("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// a.PanicsWithErrorf("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func (a *Assertions) PanicsWithErrorf(errString string, f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1301,7 +1385,7 @@ func (a *Assertions) PanicsWithErrorf(errString string, f assert.PanicTestFunc, // PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// a.PanicsWithValue("crazy error", func(){ GoCrazy() }) +// a.PanicsWithValue("crazy error", func(){ GoCrazy() }) func (a *Assertions) PanicsWithValue(expected interface{}, f assert.PanicTestFunc, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1312,7 +1396,7 @@ func (a *Assertions) PanicsWithValue(expected interface{}, f assert.PanicTestFun // PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that // the recovered panic value equals the expected panic value. // -// a.PanicsWithValuef("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") +// a.PanicsWithValuef("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") func (a *Assertions) PanicsWithValuef(expected interface{}, f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1322,7 +1406,7 @@ func (a *Assertions) PanicsWithValuef(expected interface{}, f assert.PanicTestFu // Panicsf asserts that the code inside the specified PanicTestFunc panics. // -// a.Panicsf(func(){ GoCrazy() }, "error message %s", "formatted") +// a.Panicsf(func(){ GoCrazy() }, "error message %s", "formatted") func (a *Assertions) Panicsf(f assert.PanicTestFunc, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1332,8 +1416,8 @@ func (a *Assertions) Panicsf(f assert.PanicTestFunc, msg string, args ...interfa // Positive asserts that the specified element is positive // -// a.Positive(1) -// a.Positive(1.23) +// a.Positive(1) +// a.Positive(1.23) func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1343,8 +1427,8 @@ func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) { // Positivef asserts that the specified element is positive // -// a.Positivef(1, "error message %s", "formatted") -// a.Positivef(1.23, "error message %s", "formatted") +// a.Positivef(1, "error message %s", "formatted") +// a.Positivef(1.23, "error message %s", "formatted") func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1354,8 +1438,8 @@ func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) { // Regexp asserts that a specified regexp matches a string. // -// a.Regexp(regexp.MustCompile("start"), "it's starting") -// a.Regexp("start...$", "it's not starting") +// a.Regexp(regexp.MustCompile("start"), "it's starting") +// a.Regexp("start...$", "it's not starting") func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1365,8 +1449,8 @@ func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...inter // Regexpf asserts that a specified regexp matches a string. // -// a.Regexpf(regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") -// a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") +// a.Regexpf(regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") +// a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1376,7 +1460,7 @@ func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args . // Same asserts that two pointers reference the same object. // -// a.Same(ptr1, ptr2) +// a.Same(ptr1, ptr2) // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1389,7 +1473,7 @@ func (a *Assertions) Same(expected interface{}, actual interface{}, msgAndArgs . // Samef asserts that two pointers reference the same object. // -// a.Samef(ptr1, ptr2, "error message %s", "formatted") +// a.Samef(ptr1, ptr2, "error message %s", "formatted") // // Both arguments must be pointer variables. Pointer variable sameness is // determined based on the equality of both type and value. @@ -1403,7 +1487,7 @@ func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string, // Subset asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // -// a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") +// a.Subset([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]") func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1414,7 +1498,7 @@ func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ... // Subsetf asserts that the specified list(array, slice...) contains all // elements given in the specified subset(array, slice...). // -// a.Subsetf([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") +// a.Subsetf([1, 2, 3], [1, 2], "But [1, 2, 3] does contain [1, 2]", "error message %s", "formatted") func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1424,7 +1508,7 @@ func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, a // True asserts that the specified value is true. // -// a.True(myBool) +// a.True(myBool) func (a *Assertions) True(value bool, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1434,7 +1518,7 @@ func (a *Assertions) True(value bool, msgAndArgs ...interface{}) { // Truef asserts that the specified value is true. // -// a.Truef(myBool, "error message %s", "formatted") +// a.Truef(myBool, "error message %s", "formatted") func (a *Assertions) Truef(value bool, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1444,7 +1528,7 @@ func (a *Assertions) Truef(value bool, msg string, args ...interface{}) { // WithinDuration asserts that the two times are within duration delta of each other. // -// a.WithinDuration(time.Now(), time.Now(), 10*time.Second) +// a.WithinDuration(time.Now(), time.Now(), 10*time.Second) func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1454,7 +1538,7 @@ func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta // WithinDurationf asserts that the two times are within duration delta of each other. // -// a.WithinDurationf(time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") +// a.WithinDurationf(time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1464,7 +1548,7 @@ func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta // WithinRange asserts that a time is within a time range (inclusive). // -// a.WithinRange(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) +// a.WithinRange(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() @@ -1474,7 +1558,7 @@ func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Tim // WithinRangef asserts that a time is within a time range (inclusive). // -// a.WithinRangef(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") +// a.WithinRangef(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") func (a *Assertions) WithinRangef(actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() diff --git a/vendor/github.com/timakin/bodyclose/passes/bodyclose/bodyclose.go b/vendor/github.com/timakin/bodyclose/passes/bodyclose/bodyclose.go index acf7a039..21bb485b 100644 --- a/vendor/github.com/timakin/bodyclose/passes/bodyclose/bodyclose.go +++ b/vendor/github.com/timakin/bodyclose/passes/bodyclose/bodyclose.go @@ -127,7 +127,12 @@ func (r *runner) isopen(b *ssa.BasicBlock, i int) bool { resRefs := *val.Referrers() for _, resRef := range resRefs { switch resRef := resRef.(type) { - case *ssa.Store: // Call in Closure function + case *ssa.Store: // Call in Closure function / Response is global variable + if _, ok := resRef.Addr.(*ssa.Global); ok { + // Referrers for globals are always nil, so skip. + return false + } + if len(*resRef.Addr.Referrers()) == 0 { return true } @@ -218,6 +223,10 @@ func (r *runner) getResVal(instr ssa.Instruction) (ssa.Value, bool) { if instr.Type().String() == r.resTyp.String() { return instr, true } + case *ssa.Store: + if instr.Val.Type().String() == r.resTyp.String() { + return instr.Val, true + } } return nil, false } diff --git a/vendor/github.com/xen0n/gosmopolitan/.editorconfig b/vendor/github.com/xen0n/gosmopolitan/.editorconfig new file mode 100644 index 00000000..0c0f7e7e --- /dev/null +++ b/vendor/github.com/xen0n/gosmopolitan/.editorconfig @@ -0,0 +1,23 @@ +# EditorConfig is awesome: http://EditorConfig.org + +root = true + +[*] +indent_style = space +trim_trailing_whitespace = true +end_of_line = lf +insert_final_newline = true +charset = utf-8 + +[{*.sh,*.md}] +indent_size = 4 + +[{*.yaml,*.yml}] +indent_size = 2 + +# hard tabs for Go and Makefile per best practice of file format +[*.go] +indent_style = tab + +[Makefile] +indent_style = tab diff --git a/vendor/github.com/xen0n/gosmopolitan/.gitignore b/vendor/github.com/xen0n/gosmopolitan/.gitignore new file mode 100644 index 00000000..1a1abaa2 --- /dev/null +++ b/vendor/github.com/xen0n/gosmopolitan/.gitignore @@ -0,0 +1,188 @@ +# ignore the local build artifact +/gosmopolitan + +# and test artifacts +/coverage.txt + +# the following are auto-generated + +# Created by https://www.toptal.com/developers/gitignore/api/go,visualstudiocode,vim,goland +# Edit at https://www.toptal.com/developers/gitignore?templates=go,visualstudiocode,vim,goland + +### Go ### +# If you prefer the allow list template instead of the deny list, see community template: +# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore +# +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Go workspace file +go.work + +### GoLand ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### GoLand Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint +.idea/**/sonarlint/ + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator-enh.xml +.idea/**/markdown-navigator/ + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 +.idea/$CACHE_FILE$ + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream +.idea/codestream.xml + +# Azure Toolkit for IntelliJ plugin +# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij +.idea/**/azureSettings.xml + +### Vim ### +# Swap +[._]*.s[a-v][a-z] +!*.svg # comment out if you don't need vector files +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + +# Session +Session.vim +Sessionx.vim + +# Temporary +.netrwhist +*~ +# Auto-generated tag files +tags +# Persistent undo +[._]*.un~ + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +# End of https://www.toptal.com/developers/gitignore/api/go,visualstudiocode,vim,goland diff --git a/vendor/github.com/xen0n/gosmopolitan/.golangci.yml b/vendor/github.com/xen0n/gosmopolitan/.golangci.yml new file mode 100644 index 00000000..0bce1250 --- /dev/null +++ b/vendor/github.com/xen0n/gosmopolitan/.golangci.yml @@ -0,0 +1,31 @@ +run: + go: '1.19' + modules-download-mode: readonly + +linters: + enable: + - goheader + - goimports + - gosec + - gosimple + - lll + - nakedret + - revive + - stylecheck + - unused + +linters-settings: + goheader: + template: |- + SPDX-License-Identifier: GPL-3.0-or-later + goimports: + local-prefixes: github.com/xen0n/gosmopolitan + gosimple: + go: '1.19' + lll: + line-length: 120 + tab-width: 4 + nakedret: + max-func-lines: 1 + stylecheck: + go: '1.19' diff --git a/vendor/github.com/xen0n/gosmopolitan/LICENSE b/vendor/github.com/xen0n/gosmopolitan/LICENSE new file mode 100644 index 00000000..94a9ed02 --- /dev/null +++ b/vendor/github.com/xen0n/gosmopolitan/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/vendor/github.com/xen0n/gosmopolitan/README.md b/vendor/github.com/xen0n/gosmopolitan/README.md new file mode 100644 index 00000000..93e3701e --- /dev/null +++ b/vendor/github.com/xen0n/gosmopolitan/README.md @@ -0,0 +1,135 @@ +# gosmopolitan + +![GitHub Workflow Status (main branch)](https://img.shields.io/github/actions/workflow/status/xen0n/gosmopolitan/go.yml?branch=main) +![Codecov](https://img.shields.io/codecov/c/gh/xen0n/gosmopolitan) +![GitHub license info](https://img.shields.io/github/license/xen0n/gosmopolitan) +![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/xen0n/gosmopolitan) +[![Go Report Card](https://goreportcard.com/badge/github.com/xen0n/gosmopolitan)](https://goreportcard.com/report/github.com/xen0n/gosmopolitan) +[![Go Reference](https://pkg.go.dev/badge/github.com/xen0n/gosmopolitan.svg)](https://pkg.go.dev/github.com/xen0n/gosmopolitan) + +[简体中文](./README.zh-Hans.md) + +`gosmopolitan` checks your Go codebase for code smells that may prove to be +hindrance to internationalization ("i18n") and/or localization ("l10n"). + +The name is a wordplay on "cosmopolitan". + +## Checks + +Currently `gosmopolitan` checks for the following anti-patterns: + +* Occurrences of string literals containing characters from certain writing + systems. + + Existence of such strings often means the relevant logic is hard to + internationalize, or at least, require special care when doing i18n/l10n. + +* Usages of `time.Local`. + + An internationalized app or library should almost never process time and + date values in the timezone in which it is running; instead one should use + the respective user preference, or the timezone as dictated by the domain + logic. + +Note that local times are produced in a lot more ways than via direct casts to +`time.Local` alone, such as: + +* `time.LoadLocation("Local")` +* received from a `time.Ticker` +* functions explicitly documented to return local times + * `time.Now()` + * `time.Unix()` + * `time.UnixMilli()` + * `time.UnixMicro()` + +Proper identification of these use cases require a fairly complete dataflow +analysis pass, which is not implemented currently. In addition, right now you +have to pay close attention to externally-provided time values (such as from +your framework like Gin or gRPC) as they are not properly tracked either. + +## Caveats + +Note that the checks implemented here are only suitable for codebases with the +following characteristics, and may not suit your particular project's needs: + +* Originally developed for an audience using non-Latin writing system(s), +* Returns bare strings intended for humans containing such non-Latin characters, and +* May occasionally (or frequently) refer to the local timezone. + +For example, the lints may prove valuable if you're revamping a web service +originally targetting the Chinese market (hence producing strings with Chinese +characters all over the place) to be more i18n-aware. Conversely, if you want +to identify some of the i18n-naïve places in an English-only app, the linter +will output nothing. + +## golangci-lint integration + +`gosmopolitan` is not integrated into [`golangci-lint`][gcl-home] yet, but +you can nevertheless run it [as a custom plugin][gcl-plugin]. + +[gcl-home]: https://golangci-lint.run +[gcl-plugin]: https://golangci-lint.run/contributing/new-linters/#how-to-add-a-private-linter-to-golangci-lint + +First make yourself a plugin `.so` file like this: + +```go +// compile this with something like `go build -buildmode=plugin` + +package main + +import ( + "github.com/xen0n/gosmopolitan" + "golang.org/x/tools/go/analysis" +) + +type analyzerPlugin struct{} + +func (analyzerPlugin) GetAnalyzers() []*analysis.Analyzer { + // You can customize the options via gosmopolitan.NewAnalyzerWithConfig + // instead. + return []*analysis.Analyzer{ + gosmopolitan.DefaultAnalyzer, + } +} + +var AnalyzerPlugin analyzerPlugin +``` + +You just need to make sure the `golang.org/x/tools` version used to build the +plugin is consistent with that of your `golangci-lint` binary. (Of course the +`golangci-lint` binary should be built with plugin support enabled too; +notably, [the Homebrew `golangci-lint` is built without plugin support][hb-issue], +so beware of this.) + +[hb-issue]: https://github.com/golangci/golangci-lint/issues/1182 + +|`golangci-lint` version|`gosmopolitan` tag to use| +|-----------------------|-------------------------| +|1.50.x|v1.0.0| + +Then reference it in your `.golangci.yml`, and enable it in the `linters` +section: + +```yaml +linters: + # ... + enable: + # ... + - gosmopolitan + # ... + +linters-settings: + custom: + gosmopolitan: + path: 'path/to/your/plugin.so' + description: 'Report certain i18n/l10n anti-patterns in your Go codebase' + original-url: 'https://github.com/xen0n/gosmopolitan' + # ... +``` + +Then you can `golangci-lint run` and `//nolint:gosmopolitan` as you would +with any other supported linter. + +## License + +`gosmopolitan` is licensed under the GPL license, version 3 or later. diff --git a/vendor/github.com/xen0n/gosmopolitan/README.zh-Hans.md b/vendor/github.com/xen0n/gosmopolitan/README.zh-Hans.md new file mode 100644 index 00000000..7f1b7b7a --- /dev/null +++ b/vendor/github.com/xen0n/gosmopolitan/README.zh-Hans.md @@ -0,0 +1,121 @@ +# gosmopolitan + +![GitHub Workflow Status (main branch)](https://img.shields.io/github/actions/workflow/status/xen0n/gosmopolitan/go.yml?branch=main) +![Codecov](https://img.shields.io/codecov/c/gh/xen0n/gosmopolitan) +![GitHub license info](https://img.shields.io/github/license/xen0n/gosmopolitan) +![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/xen0n/gosmopolitan) +[![Go Report Card](https://goreportcard.com/badge/github.com/xen0n/gosmopolitan)](https://goreportcard.com/report/github.com/xen0n/gosmopolitan) +[![Go Reference](https://pkg.go.dev/badge/github.com/xen0n/gosmopolitan.svg)](https://pkg.go.dev/github.com/xen0n/gosmopolitan) + +[English](./README.md) + +用 `gosmopolitan` 检查你的 Go 代码库里有没有国际化(“i18n“)或者本地化(”l10n“)的阻碍。 + +项目名字来自“cosmopolitan”的文字游戏。 + +## 检查 + +`gosmopolitan` 目前会检查以下的反模式(anti-patterns): + +* 含有来自特定书写系统字符的字符串字面量(string literals)。 + + 项目中存在这种字符串,通常意味着相关的逻辑不便于国际化,或者至少在国际化/本地化适配过程中会涉及特殊对待。 + +* `time.Local` 的使用。 + + 支持国际化的应用或程序库,几乎永远不应以程序当前运行环境的时区来处理时间、日期数据。 + 相反,在这种场景下,开发者应该使用相应的用户偏好,或者按照领域逻辑确定应该使用的时区。 + +注意:除了直接向 `time.Local` 转换之外,还有很多其他写法会产生本地时区的时刻,例如: + +* `time.LoadLocation("Local")` +* 从 `time.Ticker` 收到的值 +* 文档中明确了会返回本地时刻的函数 + * `time.Now()` + * `time.Unix()` + * `time.UnixMilli()` + * `time.UnixMicro()` + +为了正确识别这些使用场景,需要有一个相当完善的数据流分析 pass,目前还没实现。 +此外,当前您还需要自行密切注意从外部传入的时刻值(例如从您使用的 Gin 或 gRPC +之类框架传来的那些),因为这些值当前也没有被正确跟踪。 + +## 注意事项 + +请注意,本库中实现的检查仅适用于具有以下性质的代码库,因此可能不适用于您的具体场景: + +* 项目原先是为使用非拉丁字母书写系统的受众群体开发的, +* 项目会返回包含这些非拉丁字母字符的裸的字符串(即,未经处理或变换的), +* 项目可能偶尔(或者经常)引用程序当前运行环境的本地时区。 + +举个例子:如果您在翻新一个本来面向中国用户群体(因此到处都在产生含有汉字的字符串)的 +web 服务,以使其更加国际化,这里的 lints 可能会很有价值。 +反之,如果您想在一个仅支持英语的应用里,寻找其中不利于国际化的那部分写法,本 +linter 则什么都不会输出。 + +## 与 golangci-lint 集成 + +`gosmopolitan` 目前没有集成进上游 [`golangci-lint`][gcl-home],但您仍然可以[以自定义插件的方式][gcl-plugin]使用本项目。 + +[gcl-home]: https://golangci-lint.run +[gcl-plugin]: https://golangci-lint.run/contributing/new-linters/#how-to-add-a-private-linter-to-golangci-lint + +首先像这样做一个插件 `.so` 文件: + +```go +// 用类似 `go build -buildmode=plugin` 的方式编译 + +package main + +import ( + "github.com/xen0n/gosmopolitan" + "golang.org/x/tools/go/analysis" +) + +type analyzerPlugin struct{} + +func (analyzerPlugin) GetAnalyzers() []*analysis.Analyzer { + // 你可以用 gosmopolitan.NewAnalyzer 来自定义配置。 + return []*analysis.Analyzer{ + gosmopolitan.DefaultAnalyzer, + } +} + +var AnalyzerPlugin analyzerPlugin +``` + +您只需要保证构建时使用的 `golang.org/x/tools` 模块版本和您的 `golangci-lint` +二进制的相应模块版本一致。(当然,`golangci-lint` 二进制也应该包含插件支持; +[Homebrew 的 `golangci-lint` 没有插件支持][hb-issue],尤其需要注意。) + +[hb-issue]: https://github.com/golangci/golangci-lint/issues/1182 + +|`golangci-lint` 版本|对应可用的 `gosmopolitan` tag| +|--------------------|-----------------------------| +|1.50.x|v1.0.0| + +然后在您的 `.golangci.yml` 中引用它,在 `linters` 一节中启用它: + +```yaml +linters: + # ... + enable: + # ... + - gosmopolitan + # ... + +linters-settings: + custom: + gosmopolitan: + path: 'path/to/your/plugin.so' + description: 'Report certain i18n/l10n anti-patterns in your Go codebase' + original-url: 'https://github.com/xen0n/gosmopolitan' + # ... +``` + +这样您就可以像使用其他 linters 一样 `golangci-lint run` 和 +`//nolint:gosmopolitan` 了。 + +## 许可证 + +`gosmopolitan` 以 GPL v3 或更新的版本许可使用。 diff --git a/vendor/github.com/xen0n/gosmopolitan/lib.go b/vendor/github.com/xen0n/gosmopolitan/lib.go new file mode 100644 index 00000000..67b1151c --- /dev/null +++ b/vendor/github.com/xen0n/gosmopolitan/lib.go @@ -0,0 +1,385 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package gosmopolitan + +import ( + "fmt" + "go/ast" + "go/token" + "go/types" + "regexp" + "strings" + "unicode" + + "golang.org/x/text/runes" + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/inspector" +) + +const analyzerName = "gosmopolitan" +const analyzerDoc = "Report certain i18n/l10n anti-patterns in your Go codebase" + +type AnalyzerConfig struct { + // LookAtTests is flag controlling whether the lints are going to look at + // test files, despite other config knobs of the Go analysis tooling + // framework telling us otherwise. + // + // By default gosmopolitan does not look at test files, because i18n-aware + // apps most probably have many unmarked strings in test cases, and names + // and descriptions *of* test cases are probably in the program's original + // natural language too. + LookAtTests bool + // EscapeHatches is optionally a list of fully qualified names, in the + // `(full/pkg/path).name` form, to act as "i18n escape hatches". Inside + // call-like expressions to those names, the string literal script check + // is ignored. + // + // With this functionality in place, you can use type aliases like + // `type R = string` as markers, or have explicitly i18n-aware functions + // exempt from the checks. + EscapeHatches []string + // WatchForScripts is optionally a list of Unicode script names to watch + // for any usage in string literals. The range of supported scripts is + // determined by the [unicode.Scripts] map and values are case-sensitive. + WatchForScripts []string + // AllowTimeLocal is flag controlling whether usages of [time.Local] are + // allowed (i.e. not reported). + AllowTimeLocal bool +} + +func NewAnalyzer() *analysis.Analyzer { + var lookAtTests bool + var escapeHatchesStr string + var watchForScriptsStr string + var allowTimeLocal bool + + a := &analysis.Analyzer{ + Name: analyzerName, + Doc: analyzerDoc, + Requires: []*analysis.Analyzer{ + inspect.Analyzer, + }, + Run: func(p *analysis.Pass) (any, error) { + cfg := AnalyzerConfig{ + LookAtTests: lookAtTests, + EscapeHatches: strings.Split(escapeHatchesStr, ","), + WatchForScripts: strings.Split(watchForScriptsStr, ","), + AllowTimeLocal: allowTimeLocal, + } + pctx := processCtx{cfg: &cfg, p: p} + return pctx.run() + }, + RunDespiteErrors: false, + } + + a.Flags.BoolVar(&lookAtTests, + "lookattests", + false, + "also check the test files", + ) + a.Flags.StringVar( + &escapeHatchesStr, + "escapehatches", + "", + "comma-separated list of fully qualified names to act as i18n escape hatches", + ) + a.Flags.StringVar( + &watchForScriptsStr, + "watchforscripts", + "Han", + "comma-separated list of Unicode scripts to watch out for occurrence in string literals", + ) + a.Flags.BoolVar(&allowTimeLocal, + "allowtimelocal", + false, + "allow time.Local usages", + ) + + return a +} + +func NewAnalyzerWithConfig(cfg *AnalyzerConfig) *analysis.Analyzer { + return &analysis.Analyzer{ + Name: analyzerName, + Doc: analyzerDoc, + Requires: []*analysis.Analyzer{ + inspect.Analyzer, + }, + Run: func(p *analysis.Pass) (any, error) { + pctx := processCtx{cfg: cfg, p: p} + return pctx.run() + }, + RunDespiteErrors: false, + } +} + +var DefaultAnalyzer = NewAnalyzer() + +func validateUnicodeScriptName(name string) error { + if _, ok := unicode.Scripts[name]; !ok { + return fmt.Errorf("invalid Unicode script name: %s", name) + } + return nil +} + +// example input: ["Han", "Arabic"] +// example output: `\p{Han}|\p{Arabic}` +// assumes len(scriptNames) > 0 +func makeUnicodeScriptMatcherRegexpString(scriptNames []string) string { + var sb strings.Builder + for i, s := range scriptNames { + if i > 0 { + sb.WriteRune('|') + } + sb.WriteString(`\p{`) + sb.WriteString(s) + sb.WriteRune('}') + } + return sb.String() +} + +func makeUnicodeScriptMatcherRegexp(scriptNames []string) (*regexp.Regexp, error) { + return regexp.Compile(makeUnicodeScriptMatcherRegexpString(scriptNames)) +} + +type processCtx struct { + cfg *AnalyzerConfig + p *analysis.Pass +} + +func mapSlice[T any, U any](x []T, fn func(T) U) []U { + if x == nil { + return nil + } + y := make([]U, len(x)) + for i, v := range x { + y[i] = fn(v) + } + return y +} + +func sliceToSet[T comparable](x []T) map[T]struct{} { + // lo.SliceToMap(x, func(k T) (T, struct{}) { return k, struct{}{} }) + y := make(map[T]struct{}, len(x)) + for _, k := range x { + y[k] = struct{}{} + } + return y +} + +func getFullyQualifiedName(x types.Object) string { + pkg := x.Pkg() + if pkg == nil { + return x.Name() + } + return fmt.Sprintf("%s.%s", pkg.Path(), x.Name()) +} + +// if input is in the "(%s).%s" form, remove the parens, else return the +// unchanged input +// +// this is for maintaining compatibility with the previous FQN notation that +// was born out of my confusion (the previous notation, while commonly seen, +// seems to be only for methods or pointer receiver types; the parens-less +// form is in fact unambiguous, because Go identifiers can't contain periods.) +func unquoteInputFQN(x string) string { + if len(x) == 0 || x[0] != '(' { + return x + } + + before, after, found := strings.Cut(x[1:], ")") + if !found { + // malformed input: string in "(xxxxx" form with unclosed parens! + // in this case, only removing the opening parens might be better than + // doing nothing after all + return x[1:] + } + + // at this point, + // input: "(foo).bar" + // before: "foo" + // after: ".bar" + return before + after +} + +func (c *processCtx) run() (any, error) { + escapeHatchesSet := sliceToSet(mapSlice(c.cfg.EscapeHatches, unquoteInputFQN)) + + if len(c.cfg.WatchForScripts) == 0 { + c.cfg.WatchForScripts = []string{"Han"} + } + + for _, s := range c.cfg.WatchForScripts { + if err := validateUnicodeScriptName(s); err != nil { + return nil, err + } + } + + charRE, err := makeUnicodeScriptMatcherRegexp(c.cfg.WatchForScripts) + if err != nil { + return nil, err + } + + usq := newUnicodeScriptQuerier(c.cfg.WatchForScripts) + + insp := c.p.ResultOf[inspect.Analyzer].(*inspector.Inspector) + + // support ignoring the test files, because test files could be full of + // i18n and l10n fixtures, and we want to focus on the actual run-time + // logic + // + // TODO: is there a way to both ignore test files earlier, and make use of + // inspect.Analyzer's cached results? currently Inspector doesn't provide + // a way to selectively travese some files' AST but not others. + isBelongingToTestFiles := func(n ast.Node) bool { + return strings.HasSuffix(c.p.Fset.File(n.Pos()).Name(), "_test.go") + } + + shouldSkipTheContainingFile := func(n ast.Node) bool { + if c.cfg.LookAtTests { + return false + } + return isBelongingToTestFiles(n) + } + + insp.Nodes(nil, func(n ast.Node, push bool) bool { + // we only need to look at each node once + if !push { + return false + } + + if shouldSkipTheContainingFile(n) { + return false + } + + // skip blocks that can contain string literals but are not otherwise + // interesting for us + switch n.(type) { + case *ast.ImportSpec, *ast.TypeSpec: + // import blocks, type declarations + return false + } + + // and don't look inside escape hatches + referentFQN := c.getFullyQualifiedNameOfReferent(n) + if referentFQN != "" { + _, isEscapeHatch := escapeHatchesSet[referentFQN] + // if isEscapeHatch: don't recurse (false) + return !isEscapeHatch + } + + // check only string literals + lit, ok := n.(*ast.BasicLit) + if !ok { + return true + } + if lit.Kind != token.STRING { + return true + } + + // report string literals containing characters of given script (in + // the sense of "writing system") + if charRE.MatchString(lit.Value) { + match := charRE.FindIndex([]byte(lit.Value)) + matchCh := []byte(lit.Value)[match[0]:match[1]] + scriptName := usq.queryScriptForRuneBytes(matchCh) + + c.p.Report(analysis.Diagnostic{ + Pos: lit.Pos() + token.Pos(match[0]), + End: lit.Pos() + token.Pos(match[1]), + Message: fmt.Sprintf("string literal contains rune in %s script", scriptName), + }) + } + + return true + }) + + if !c.cfg.AllowTimeLocal { + // check time.Local usages + insp.Nodes([]ast.Node{(*ast.Ident)(nil)}, func(n ast.Node, push bool) bool { + // we only need to look at each node once + if !push { + return false + } + + if shouldSkipTheContainingFile(n) { + return false + } + + ident := n.(*ast.Ident) + + d := c.p.TypesInfo.ObjectOf(ident) + if d == nil || d.Pkg() == nil { + return true + } + + if d.Pkg().Path() == "time" && d.Name() == "Local" { + c.p.Report(analysis.Diagnostic{ + Pos: n.Pos(), + End: n.End(), + Message: "usage of time.Local", + }) + } + + return true + }) + } + + return nil, nil +} + +func (c *processCtx) getFullyQualifiedNameOfReferent(n ast.Node) string { + var ident *ast.Ident + switch e := n.(type) { + case *ast.CallExpr: + ident = getIdentOfTypeOfExpr(e.Fun) + + case *ast.CompositeLit: + ident = getIdentOfTypeOfExpr(e.Type) + + default: + return "" + } + + referent := c.p.TypesInfo.Uses[ident] + if referent == nil { + return "" + } + + return getFullyQualifiedName(referent) +} + +func getIdentOfTypeOfExpr(e ast.Expr) *ast.Ident { + switch x := e.(type) { + case *ast.Ident: + return x + case *ast.SelectorExpr: + return x.Sel + } + return nil +} + +type unicodeScriptQuerier struct { + sets map[string]runes.Set +} + +func newUnicodeScriptQuerier(scriptNames []string) *unicodeScriptQuerier { + sets := make(map[string]runes.Set, len(scriptNames)) + for _, s := range scriptNames { + sets[s] = runes.In(unicode.Scripts[s]) + } + return &unicodeScriptQuerier{ + sets: sets, + } +} + +func (x *unicodeScriptQuerier) queryScriptForRuneBytes(b []byte) string { + r := []rune(string(b))[0] + for s, set := range x.sets { + if set.Contains(r) { + return s + } + } + return "" +} diff --git a/vendor/github.com/ykadowak/zerologlint/.goreleaser.yaml b/vendor/github.com/ykadowak/zerologlint/.goreleaser.yaml new file mode 100644 index 00000000..c1b23f00 --- /dev/null +++ b/vendor/github.com/ykadowak/zerologlint/.goreleaser.yaml @@ -0,0 +1,31 @@ +before: + hooks: + - go mod tidy +builds: + - id: zerologlint + main: ./cmd/zerologlint + binary: zerologlint + env: + - CGO_ENABLED=0 + goos: + - linux + - windows + - darwin +archives: + - replacements: + darwin: Darwin + linux: Linux + windows: Windows + 386: i386 + amd64: x86_64 +checksum: + name_template: 'checksums.txt' +snapshot: + name_template: "{{ incpatch .Version }}-next" +changelog: + sort: asc + filters: + exclude: + - '^docs:' + - '^test:' + - '^ci:' diff --git a/vendor/github.com/ykadowak/zerologlint/LICENSE b/vendor/github.com/ykadowak/zerologlint/LICENSE new file mode 100644 index 00000000..92a1e3b3 --- /dev/null +++ b/vendor/github.com/ykadowak/zerologlint/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Yusuke Kadowaki + +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/vendor/github.com/ykadowak/zerologlint/README.md b/vendor/github.com/ykadowak/zerologlint/README.md new file mode 100644 index 00000000..d571ba31 --- /dev/null +++ b/vendor/github.com/ykadowak/zerologlint/README.md @@ -0,0 +1,46 @@ +# zerologlint +![build](https://github.com/ykadowak/zerologlint/actions/workflows/testing.yaml/badge.svg) + +`zerologlint` is a linter for [zerolog](https://github.com/rs/zerolog) that can be run with `go vet`. +It detects the wrong usage of `zerolog` that a user forgets to dispatch `zerolog.Event` with `Send` or `Msg` function, in which case nothing will be logged. For more detailed explanations of the cases it detects, see [Example](#Example). + +## Install + +```bash +go install github.com/ykadowak/zerologlint/cmd/zerologlint@latest +``` + +## Usage +```bash +go vet -vettool=`which zerologlint` ./... +``` + +## Example +```go +package main + +import ( + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" +) + +func main() { + // 1. Basic case + log.Info() // "must be dispatched by Msg or Send method" + + // 2. Nested case + log.Info(). // "must be dispatched by Msg or Send method" + Str("foo", "bar"). + Dict("dict", zerolog.Dict(). + Str("bar", "baz"). + Int("n", 1), + ) + + // 3. Reassignment case + logger := log.Info() // "must be dispatched by Msg or Send method" + if err != nil { + logger = log.Error() // "must be dispatched by Msg or Send method" + } + logger.Str("foo", "bar") +} +``` diff --git a/vendor/github.com/ykadowak/zerologlint/zerologlint.go b/vendor/github.com/ykadowak/zerologlint/zerologlint.go new file mode 100644 index 00000000..7b9f0eea --- /dev/null +++ b/vendor/github.com/ykadowak/zerologlint/zerologlint.go @@ -0,0 +1,123 @@ +package zerologlint + +import ( + "strings" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/buildssa" + "golang.org/x/tools/go/ssa" + + "github.com/gostaticanalysis/comment/passes/commentmap" +) + +var Analyzer = &analysis.Analyzer{ + Name: "zerologlinter", + Doc: "finds cases where zerolog methods are not followed by Msg or Send", + Run: run, + Requires: []*analysis.Analyzer{ + buildssa.Analyzer, + commentmap.Analyzer, + }, +} + +func run(pass *analysis.Pass) (interface{}, error) { + srcFuncs := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs + + // This map holds all the ssa block that is a zerolog.Event type instance + // that should be dispatched. + // Everytime the zerolog.Event is dispatched with Msg() or Send(), + // deletes that block from this map. + // At the end, check if the set is empty, or report the not dispatched block. + set := make(map[ssa.Value]struct{}) + + for _, sf := range srcFuncs { + for _, b := range sf.Blocks { + for _, instr := range b.Instrs { + if c, ok := instr.(*ssa.Call); ok { + v := c.Value() + // check if it's in github.com/rs/zerolog/log since there's some + // functions in github.com/rs/zerolog that returns zerolog.Event + // which should not be included + if isInLogPkg(v) { + if isZerologEvent(v) { + // check if this is a new instance of zerolog.Event like logger := log.Error() + // which should be dispatched afterwards at some point + if len(v.Call.Args) == 0 { + set[v] = struct{}{} + } + continue + } + } + + // if the call does not return zerolog.Event, + // check if the base is zerolog.Event. + // if so, check if the StaticCallee is Send() or Msg(). + // if so, remove the arg[0] from the set. + for _, arg := range v.Call.Args { + if isZerologEvent(arg) { + if isDispatchMethod(v) { + val := getRootSsaValue(arg) + // if there's branch, remove both ways from the set + if phi, ok := val.(*ssa.Phi); ok { + for _, edge := range phi.Edges { + delete(set, edge) + } + } else { + delete(set, val) + } + } + } + } + } + } + } + } + // At the end, if the set is clear -> ok. + // Otherwise, there must be a left zerolog.Event var that weren't dispached. So report it. + for k := range set { + pass.Reportf(k.Pos(), "must be dispatched by Msg or Send method") + } + return nil, nil +} + +func isInLogPkg(c *ssa.Call) bool { + switch v := c.Call.Value.(type) { + case ssa.Member: + p := v.Package() + if p == nil { + return false + } + return strings.HasSuffix(p.Pkg.Path(), "github.com/rs/zerolog/log") + default: + return false + } +} + +func isZerologEvent(v ssa.Value) bool { + ts := v.Type().String() + return strings.HasSuffix(ts, "github.com/rs/zerolog.Event") +} + +func isDispatchMethod(c *ssa.Call) bool { + m := c.Common().StaticCallee().Name() + if m == "Send" || m == "Msg" || m == "Msgf" || m == "MsgFunc" { + return true + } + return false +} + +func getRootSsaValue(v ssa.Value) ssa.Value { + if c, ok := v.(*ssa.Call); ok { + v := c.Value() + // When there is no receiver, that's the block of zerolog.Event + // eg. Error() method in log.Error().Str("foo", "bar"). Send() + if len(v.Call.Args) == 0 { + return v + } + + // Ok to just return the receiver because all the method in this + // chain is zerolog.Event at this point. + return getRootSsaValue(v.Call.Args[0]) + } + return v +} diff --git a/vendor/github.com/junk1tm/musttag/.golangci.yml b/vendor/go.tmz.dev/musttag/.golangci.yml similarity index 100% rename from vendor/github.com/junk1tm/musttag/.golangci.yml rename to vendor/go.tmz.dev/musttag/.golangci.yml diff --git a/vendor/go.tmz.dev/musttag/.goreleaser.yml b/vendor/go.tmz.dev/musttag/.goreleaser.yml new file mode 100644 index 00000000..25908088 --- /dev/null +++ b/vendor/go.tmz.dev/musttag/.goreleaser.yml @@ -0,0 +1,27 @@ +builds: + - main: ./cmd/musttag + env: + - CGO_ENABLED=0 + flags: + - -trimpath + ldflags: + - -s -w -X main.version={{ .Version }} + targets: + - darwin_amd64 + - darwin_arm64 + - linux_amd64 + - windows_amd64 + +archives: + - format_overrides: + - goos: windows + format: zip + +brews: + - tap: + owner: tmzane + name: homebrew-tap + token: '{{ .Env.HOMEBREW_TAP_TOKEN }}' + homepage: https://github.com/tmzane/musttag + description: A Go linter that enforces field tags in (un)marshaled structs + license: MPL-2.0 diff --git a/vendor/go.tmz.dev/musttag/LICENSE b/vendor/go.tmz.dev/musttag/LICENSE new file mode 100644 index 00000000..a612ad98 --- /dev/null +++ b/vendor/go.tmz.dev/musttag/LICENSE @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/vendor/go.tmz.dev/musttag/README.md b/vendor/go.tmz.dev/musttag/README.md new file mode 100644 index 00000000..621c7a9a --- /dev/null +++ b/vendor/go.tmz.dev/musttag/README.md @@ -0,0 +1,106 @@ +# musttag + +[![checks](https://github.com/tmzane/musttag/actions/workflows/checks.yml/badge.svg)](https://github.com/tmzane/musttag/actions/workflows/checks.yml) +[![pkg.go.dev](https://pkg.go.dev/badge/go.tmz.dev/musttag.svg)](https://pkg.go.dev/go.tmz.dev/musttag) +[![goreportcard](https://goreportcard.com/badge/go.tmz.dev/musttag)](https://goreportcard.com/report/go.tmz.dev/musttag) +[![codecov](https://codecov.io/gh/tmzane/musttag/branch/main/graph/badge.svg)](https://codecov.io/gh/tmzane/musttag) + +A Go linter that enforces field tags in (un)marshaled structs + +## 📌 About + +`musttag` checks that exported fields of a struct passed to a `Marshal`-like function are annotated with the relevant tag: + +```go +// BAD: +var user struct { + Name string +} +data, err := json.Marshal(user) + +// GOOD: +var user struct { + Name string `json:"name"` +} +data, err := json.Marshal(user) +``` + +The rational from [Uber Style Guide][1]: + +> The serialized form of the structure is a contract between different systems. +> Changes to the structure of the serialized form, including field names, break this contract. +> Specifying field names inside tags makes the contract explicit, +> and it guards against accidentally breaking the contract by refactoring or renaming fields. + +## 🚀 Features + +The following packages are supported out of the box: + +* [`encoding/json`][2] +* [`encoding/xml`][3] +* [`gopkg.in/yaml.v3`][4] +* [`github.com/BurntSushi/toml`][5] +* [`github.com/mitchellh/mapstructure`][6] +* [`github.com/jmoiron/sqlx`][7] + +In addition, any [custom package](#custom-packages) can be added to the list. + +## 📋 Usage + +`musttag` is already integrated into `golangci-lint`, and this is the recommended way to use it. + +To enable the linter, add the following lines to `.golangci.yml`: + +```yaml +linters: + enable: + - musttag +``` + +If you'd rather prefer to use `musttag` standalone, you can install it via `brew`... + +```shell +brew install tmzane/tap/musttag +``` + +...or download a prebuilt binary from the [Releases][9] page. + +Then run it either directly or as a `go vet` tool: + +```shell +go vet -vettool=$(which musttag) ./... +``` + +### Custom packages + +To enable reporting a custom function, you need to add its description to `.golangci.yml`. + +The following is an example of adding support for the `hclsimple.DecodeFile` function from [`github.com/hashicorp/hcl`][8]: + +```yaml +linters-settings: + musttag: + functions: + # The full name of the function, including the package. + - name: github.com/hashicorp/hcl/v2/hclsimple.DecodeFile + # The struct tag whose presence should be ensured. + tag: hcl + # The position of the argument to check. + arg-pos: 2 +``` + +The same can be done via the `-fn=name:tag:arg-pos` flag when using `musttag` standalone: + +```shell +musttag -fn="github.com/hashicorp/hcl/v2/hclsimple.DecodeFile:hcl:2" ./... +``` + +[1]: https://github.com/uber-go/guide/blob/master/style.md#use-field-tags-in-marshaled-structs +[2]: https://pkg.go.dev/encoding/json +[3]: https://pkg.go.dev/encoding/xml +[4]: https://github.com/go-yaml/yaml +[5]: https://github.com/BurntSushi/toml +[6]: https://github.com/mitchellh/mapstructure +[7]: https://github.com/jmoiron/sqlx +[8]: https://github.com/hashicorp/hcl +[9]: https://github.com/tmzane/musttag/releases diff --git a/vendor/github.com/junk1tm/musttag/builtins.go b/vendor/go.tmz.dev/musttag/builtins.go similarity index 53% rename from vendor/github.com/junk1tm/musttag/builtins.go rename to vendor/go.tmz.dev/musttag/builtins.go index 50573f83..66914fa9 100644 --- a/vendor/github.com/junk1tm/musttag/builtins.go +++ b/vendor/go.tmz.dev/musttag/builtins.go @@ -37,4 +37,31 @@ var builtins = []Func{ {Name: "github.com/mitchellh/mapstructure.DecodeMetadata", Tag: "mapstructure", ArgPos: 1}, {Name: "github.com/mitchellh/mapstructure.WeakDecode", Tag: "mapstructure", ArgPos: 1}, {Name: "github.com/mitchellh/mapstructure.WeakDecodeMetadata", Tag: "mapstructure", ArgPos: 1}, + + // https://github.com/jmoiron/sqlx + {Name: "github.com/jmoiron/sqlx.Get", Tag: "db", ArgPos: 1}, + {Name: "github.com/jmoiron/sqlx.GetContext", Tag: "db", ArgPos: 2}, + {Name: "github.com/jmoiron/sqlx.Select", Tag: "db", ArgPos: 1}, + {Name: "github.com/jmoiron/sqlx.SelectContext", Tag: "db", ArgPos: 2}, + {Name: "github.com/jmoiron/sqlx.StructScan", Tag: "db", ArgPos: 1}, + {Name: "(*github.com/jmoiron/sqlx.Conn).GetContext", Tag: "db", ArgPos: 1}, + {Name: "(*github.com/jmoiron/sqlx.Conn).SelectContext", Tag: "db", ArgPos: 1}, + {Name: "(*github.com/jmoiron/sqlx.DB).Get", Tag: "db", ArgPos: 0}, + {Name: "(*github.com/jmoiron/sqlx.DB).GetContext", Tag: "db", ArgPos: 1}, + {Name: "(*github.com/jmoiron/sqlx.DB).Select", Tag: "db", ArgPos: 0}, + {Name: "(*github.com/jmoiron/sqlx.DB).SelectContext", Tag: "db", ArgPos: 1}, + {Name: "(*github.com/jmoiron/sqlx.NamedStmt).Get", Tag: "db", ArgPos: 0}, + {Name: "(*github.com/jmoiron/sqlx.NamedStmt).GetContext", Tag: "db", ArgPos: 1}, + {Name: "(*github.com/jmoiron/sqlx.NamedStmt).Select", Tag: "db", ArgPos: 0}, + {Name: "(*github.com/jmoiron/sqlx.NamedStmt).SelectContext", Tag: "db", ArgPos: 1}, + {Name: "(*github.com/jmoiron/sqlx.Row).StructScan", Tag: "db", ArgPos: 0}, + {Name: "(*github.com/jmoiron/sqlx.Rows).StructScan", Tag: "db", ArgPos: 0}, + {Name: "(*github.com/jmoiron/sqlx.Stmt).Get", Tag: "db", ArgPos: 0}, + {Name: "(*github.com/jmoiron/sqlx.Stmt).GetContext", Tag: "db", ArgPos: 1}, + {Name: "(*github.com/jmoiron/sqlx.Stmt).Select", Tag: "db", ArgPos: 0}, + {Name: "(*github.com/jmoiron/sqlx.Stmt).SelectContext", Tag: "db", ArgPos: 1}, + {Name: "(*github.com/jmoiron/sqlx.Tx).Get", Tag: "db", ArgPos: 0}, + {Name: "(*github.com/jmoiron/sqlx.Tx).GetContext", Tag: "db", ArgPos: 1}, + {Name: "(*github.com/jmoiron/sqlx.Tx).Select", Tag: "db", ArgPos: 0}, + {Name: "(*github.com/jmoiron/sqlx.Tx).SelectContext", Tag: "db", ArgPos: 1}, } diff --git a/vendor/github.com/junk1tm/musttag/musttag.go b/vendor/go.tmz.dev/musttag/musttag.go similarity index 90% rename from vendor/github.com/junk1tm/musttag/musttag.go rename to vendor/go.tmz.dev/musttag/musttag.go index 1254288f..200163f2 100644 --- a/vendor/github.com/junk1tm/musttag/musttag.go +++ b/vendor/go.tmz.dev/musttag/musttag.go @@ -3,6 +3,7 @@ package musttag import ( "flag" + "fmt" "go/ast" "go/token" "go/types" @@ -81,12 +82,12 @@ func flags(funcs *[]Func) flag.FlagSet { // for tests only. var ( - reportf = func(pass *analysis.Pass, st *structType, fn Func, fnPos token.Position) { + report = func(pass *analysis.Pass, st *structType, fn Func, fnPos token.Position) { const format = "`%s` should be annotated with the `%s` tag as it is passed to `%s` at %s" pass.Reportf(st.Pos, format, st.Name, fn.Tag, fn.shortName(), fnPos) } - // HACK(junk1tm): mainModulePackages() does not return packages from `testdata`, + // HACK: mainModulePackages() does not return packages from `testdata`, // because it is ignored by the go tool, and thus, by the `go list` command. // For tests to pass we need to add the packages with tests to the main module manually. testPackages []string @@ -106,6 +107,10 @@ func run(pass *analysis.Pass, funcs map[string]Func) (any, error) { filter := []ast.Node{(*ast.CallExpr)(nil)} walk.Preorder(filter, func(n ast.Node) { + if err != nil { + return // there is already an error. + } + call, ok := n.(*ast.CallExpr) if !ok { return // not a function call. @@ -122,7 +127,8 @@ func run(pass *analysis.Pass, funcs map[string]Func) (any, error) { } if len(call.Args) <= fn.ArgPos { - return // TODO(junk1tm): return a proper error. + err = fmt.Errorf("Func.ArgPos cannot be %d: %s accepts only %d argument(s)", fn.ArgPos, fn.Name, len(call.Args)) + return } arg := call.Args[fn.ArgPos] @@ -159,10 +165,10 @@ func run(pass *analysis.Pass, funcs map[string]Func) (any, error) { p := pass.Fset.Position(call.Pos()) p.Filename, _ = filepath.Rel(moduleDir, p.Filename) - reportf(pass, result, fn, p) + report(pass, result, fn, p) }) - return nil, nil + return nil, err } // structType is an extension for types.Struct. @@ -192,8 +198,11 @@ func (c *checker) parseStructType(t types.Type, pos token.Pos) (*structType, boo switch t := t.(type) { case *types.Named: // a struct of the named type. - pkg := t.Obj().Pkg().Path() - if _, ok := c.mainModule[pkg]; !ok { + pkg := t.Obj().Pkg() // may be nil; see issue #38. + if pkg == nil { + return nil, false + } + if _, ok := c.mainModule[pkg.Path()]; !ok { return nil, false } s, ok := t.Underlying().(*types.Struct) @@ -235,7 +244,7 @@ func (c *checker) checkStructType(st *structType, tag string) (*structType, bool } } - nested, ok := c.parseStructType(field.Type(), st.Pos) // TODO(junk1tm): or field.Pos()? + nested, ok := c.parseStructType(field.Type(), st.Pos) // TODO: or field.Pos()? if !ok { continue } diff --git a/vendor/github.com/junk1tm/musttag/utils.go b/vendor/go.tmz.dev/musttag/utils.go similarity index 84% rename from vendor/github.com/junk1tm/musttag/utils.go rename to vendor/go.tmz.dev/musttag/utils.go index fecfc43b..73e21310 100644 --- a/vendor/github.com/junk1tm/musttag/utils.go +++ b/vendor/go.tmz.dev/musttag/utils.go @@ -12,7 +12,7 @@ func mainModule() (dir string, packages map[string]struct{}, _ error) { // > When using modules, "all" expands to all packages in the main module // > and their dependencies, including dependencies needed by tests of any of those. - // NOTE(junk1tm): the command may run out of file descriptors if go version <= 1.18, + // NOTE: the command may run out of file descriptors if go version <= 1.18, // especially on macOS, which has the default soft limit set to 256 (ulimit -nS). // Since go1.19 the limit is automatically increased to the maximum allowed value; // see https://github.com/golang/go/issues/46279 for details. @@ -23,9 +23,10 @@ func mainModule() (dir string, packages map[string]struct{}, _ error) { return "", nil, fmt.Errorf("running `go list all`: %w", err) } - list := strings.TrimSpace(string(out)) - packages = make(map[string]struct{}, len(list)) - for _, pkg := range strings.Split(list, "\n") { + list := strings.Split(strings.TrimSpace(string(out)), "\n") + packages = make(map[string]struct{}, len(list)*2) + + for _, pkg := range list { packages[pkg] = struct{}{} packages[pkg+"_test"] = struct{}{} // `*_test` packages belong to the main module, see issue #24. } diff --git a/vendor/golang.org/x/crypto/cryptobyte/asn1.go b/vendor/golang.org/x/crypto/cryptobyte/asn1.go index 3141a7f1..6fc2838a 100644 --- a/vendor/golang.org/x/crypto/cryptobyte/asn1.go +++ b/vendor/golang.org/x/crypto/cryptobyte/asn1.go @@ -431,6 +431,14 @@ func (s *String) readBase128Int(out *int) bool { } ret <<= 7 b := s.read(1)[0] + + // ITU-T X.690, section 8.19.2: + // The subidentifier shall be encoded in the fewest possible octets, + // that is, the leading octet of the subidentifier shall not have the value 0x80. + if i == 0 && b == 0x80 { + return false + } + ret |= int(b & 0x7f) if b&0x80 == 0 { *out = ret diff --git a/vendor/golang.org/x/exp/slices/slices.go b/vendor/golang.org/x/exp/slices/slices.go index cff0cd49..2540bd68 100644 --- a/vendor/golang.org/x/exp/slices/slices.go +++ b/vendor/golang.org/x/exp/slices/slices.go @@ -104,8 +104,8 @@ func CompareFunc[E1, E2 any](s1 []E1, s2 []E2, cmp func(E1, E2) int) int { // Index returns the index of the first occurrence of v in s, // or -1 if not present. func Index[E comparable](s []E, v E) int { - for i, vs := range s { - if v == vs { + for i := range s { + if v == s[i] { return i } } @@ -115,8 +115,8 @@ func Index[E comparable](s []E, v E) int { // IndexFunc returns the first index i satisfying f(s[i]), // or -1 if none do. func IndexFunc[E any](s []E, f func(E) bool) int { - for i, v := range s { - if f(v) { + for i := range s { + if f(s[i]) { return i } } @@ -207,12 +207,12 @@ func Compact[S ~[]E, E comparable](s S) S { return s } i := 1 - last := s[0] - for _, v := range s[1:] { - if v != last { - s[i] = v + for k := 1; k < len(s); k++ { + if s[k] != s[k-1] { + if i != k { + s[i] = s[k] + } i++ - last = v } } return s[:i] @@ -224,12 +224,12 @@ func CompactFunc[S ~[]E, E any](s S, eq func(E, E) bool) S { return s } i := 1 - last := s[0] - for _, v := range s[1:] { - if !eq(v, last) { - s[i] = v + for k := 1; k < len(s); k++ { + if !eq(s[k], s[k-1]) { + if i != k { + s[i] = s[k] + } i++ - last = v } } return s[:i] diff --git a/vendor/golang.org/x/exp/slices/sort.go b/vendor/golang.org/x/exp/slices/sort.go index f14f40da..231b6448 100644 --- a/vendor/golang.org/x/exp/slices/sort.go +++ b/vendor/golang.org/x/exp/slices/sort.go @@ -81,10 +81,12 @@ func BinarySearch[E constraints.Ordered](x []E, target E) (int, bool) { } // BinarySearchFunc works like BinarySearch, but uses a custom comparison -// function. The slice must be sorted in increasing order, where "increasing" is -// defined by cmp. cmp(a, b) is expected to return an integer comparing the two -// parameters: 0 if a == b, a negative number if a < b and a positive number if -// a > b. +// function. The slice must be sorted in increasing order, where "increasing" +// is defined by cmp. cmp should return 0 if the slice element matches +// the target, a negative number if the slice element precedes the target, +// or a positive number if the slice element follows the target. +// cmp must implement the same ordering as the slice, such that if +// cmp(a, t) < 0 and cmp(b, t) >= 0, then a must precede b in the slice. func BinarySearchFunc[E, T any](x []E, target T, cmp func(E, T) int) (int, bool) { n := len(x) // Define cmp(x[-1], target) < 0 and cmp(x[n], target) >= 0 . diff --git a/vendor/golang.org/x/tools/go/gcexportdata/gcexportdata.go b/vendor/golang.org/x/tools/go/gcexportdata/gcexportdata.go index 165ede0f..03543bd4 100644 --- a/vendor/golang.org/x/tools/go/gcexportdata/gcexportdata.go +++ b/vendor/golang.org/x/tools/go/gcexportdata/gcexportdata.go @@ -128,15 +128,14 @@ func Read(in io.Reader, fset *token.FileSet, imports map[string]*types.Package, // (from "version"). Select appropriate importer. if len(data) > 0 { switch data[0] { - case 'i': - _, pkg, err := gcimporter.IImportData(fset, imports, data[1:], path) - return pkg, err + case 'v', 'c', 'd': // binary, till go1.10 + return nil, fmt.Errorf("binary (%c) import format is no longer supported", data[0]) - case 'v', 'c', 'd': - _, pkg, err := gcimporter.BImportData(fset, imports, data, path) + case 'i': // indexed, till go1.19 + _, pkg, err := gcimporter.IImportData(fset, imports, data[1:], path) return pkg, err - case 'u': + case 'u': // unified, from go1.20 _, pkg, err := gcimporter.UImportData(fset, imports, data[1:], path) return pkg, err diff --git a/vendor/golang.org/x/tools/go/packages/golist.go b/vendor/golang.org/x/tools/go/packages/golist.go index 6bb7168d..e84f19df 100644 --- a/vendor/golang.org/x/tools/go/packages/golist.go +++ b/vendor/golang.org/x/tools/go/packages/golist.go @@ -625,7 +625,12 @@ func (state *golistState) createDriverResponse(words ...string) (*driverResponse } if pkg.PkgPath == "unsafe" { - pkg.GoFiles = nil // ignore fake unsafe.go file + pkg.CompiledGoFiles = nil // ignore fake unsafe.go file (#59929) + } else if len(pkg.CompiledGoFiles) == 0 { + // Work around for pre-go.1.11 versions of go list. + // TODO(matloob): they should be handled by the fallback. + // Can we delete this? + pkg.CompiledGoFiles = pkg.GoFiles } // Assume go list emits only absolute paths for Dir. @@ -663,13 +668,6 @@ func (state *golistState) createDriverResponse(words ...string) (*driverResponse response.Roots = append(response.Roots, pkg.ID) } - // Work around for pre-go.1.11 versions of go list. - // TODO(matloob): they should be handled by the fallback. - // Can we delete this? - if len(pkg.CompiledGoFiles) == 0 { - pkg.CompiledGoFiles = pkg.GoFiles - } - // Temporary work-around for golang/go#39986. Parse filenames out of // error messages. This happens if there are unrecoverable syntax // errors in the source, so we can't match on a specific error message. @@ -891,6 +889,15 @@ func golistargs(cfg *Config, words []string, goVersion int) []string { // probably because you'd just get the TestMain. fmt.Sprintf("-find=%t", !cfg.Tests && cfg.Mode&findFlags == 0 && !usesExportData(cfg)), } + + // golang/go#60456: with go1.21 and later, go list serves pgo variants, which + // can be costly to compute and may result in redundant processing for the + // caller. Disable these variants. If someone wants to add e.g. a NeedPGO + // mode flag, that should be a separate proposal. + if goVersion >= 21 { + fullargs = append(fullargs, "-pgo=off") + } + fullargs = append(fullargs, cfg.BuildFlags...) fullargs = append(fullargs, "--") fullargs = append(fullargs, words...) diff --git a/vendor/golang.org/x/tools/go/packages/packages.go b/vendor/golang.org/x/tools/go/packages/packages.go index 0f1505b8..632be722 100644 --- a/vendor/golang.org/x/tools/go/packages/packages.go +++ b/vendor/golang.org/x/tools/go/packages/packages.go @@ -308,6 +308,9 @@ type Package struct { TypeErrors []types.Error // GoFiles lists the absolute file paths of the package's Go source files. + // It may include files that should not be compiled, for example because + // they contain non-matching build tags, are documentary pseudo-files such as + // unsafe/unsafe.go or builtin/builtin.go, or are subject to cgo preprocessing. GoFiles []string // CompiledGoFiles lists the absolute file paths of the package's source diff --git a/vendor/golang.org/x/tools/go/ssa/builder.go b/vendor/golang.org/x/tools/go/ssa/builder.go index ffa66673..8931fb46 100644 --- a/vendor/golang.org/x/tools/go/ssa/builder.go +++ b/vendor/golang.org/x/tools/go/ssa/builder.go @@ -363,7 +363,7 @@ func (b *builder) builtin(fn *Function, obj *types.Builtin, args []ast.Expr, typ } case "new": - alloc := emitNew(fn, deref(typ), pos) + alloc := emitNew(fn, mustDeref(typ), pos) alloc.Comment = "new" return alloc @@ -373,10 +373,8 @@ func (b *builder) builtin(fn *Function, obj *types.Builtin, args []ast.Expr, typ // We must still evaluate the value, though. (If it // was side-effect free, the whole call would have // been constant-folded.) - // - // Type parameters are always non-constant so use Underlying. - t := deref(fn.typeOf(args[0])).Underlying() - if at, ok := t.(*types.Array); ok { + t, _ := deref(fn.typeOf(args[0])) + if at, ok := typeparams.CoreType(t).(*types.Array); ok { b.expr(fn, args[0]) // for effects only return intConst(at.Len()) } @@ -431,12 +429,12 @@ func (b *builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue { return &address{addr: v, pos: e.Pos(), expr: e} case *ast.CompositeLit: - t := deref(fn.typeOf(e)) + typ, _ := deref(fn.typeOf(e)) var v *Alloc if escaping { - v = emitNew(fn, t, e.Lbrace) + v = emitNew(fn, typ, e.Lbrace) } else { - v = fn.addLocal(t, e.Lbrace) + v = fn.addLocal(typ, e.Lbrace) } v.Comment = "complit" var sb storebuf @@ -459,7 +457,7 @@ func (b *builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue { wantAddr := true v := b.receiver(fn, e.X, wantAddr, escaping, sel) index := sel.index[len(sel.index)-1] - fld := typeparams.CoreType(deref(v.Type())).(*types.Struct).Field(index) + fld := fieldOf(mustDeref(v.Type()), index) // v is an addr. // Due to the two phases of resolving AssignStmt, a panic from x.f = p() // when x is nil is required to come after the side-effects of @@ -508,7 +506,7 @@ func (b *builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue { v.setType(et) return fn.emit(v) } - return &lazyAddress{addr: emit, t: deref(et), pos: e.Lbrack, expr: e} + return &lazyAddress{addr: emit, t: mustDeref(et), pos: e.Lbrack, expr: e} case *ast.StarExpr: return &address{addr: b.expr(fn, e.X), pos: e.Star, expr: e} @@ -554,7 +552,7 @@ func (b *builder) assign(fn *Function, loc lvalue, e ast.Expr, isZero bool, sb * // so if the type of the location is a pointer, // an &-operation is implied. if _, ok := loc.(blank); !ok { // avoid calling blank.typ() - if isPointer(loc.typ()) { + if _, ok := deref(loc.typ()); ok { ptr := b.addr(fn, e, true).address(fn) // copy address if sb != nil { @@ -584,7 +582,7 @@ func (b *builder) assign(fn *Function, loc lvalue, e ast.Expr, isZero bool, sb * // Subtle: emit debug ref for aggregate types only; // slice and map are handled by store ops in compLit. - switch loc.typ().Underlying().(type) { + switch typeparams.CoreType(loc.typ()).(type) { case *types.Struct, *types.Array: emitDebugRef(fn, e, addr, true) } @@ -831,7 +829,7 @@ func (b *builder) expr0(fn *Function, e ast.Expr, tv types.TypeAndValue) Value { // The result is a "bound". obj := sel.obj.(*types.Func) rt := fn.typ(recvType(obj)) - wantAddr := isPointer(rt) + _, wantAddr := deptr(rt) escaping := true v := b.receiver(fn, e.X, wantAddr, escaping, sel) @@ -958,8 +956,9 @@ func (b *builder) stmtList(fn *Function, list []ast.Stmt) { // // escaping is defined as per builder.addr(). func (b *builder) receiver(fn *Function, e ast.Expr, wantAddr, escaping bool, sel *selection) Value { + var v Value - if wantAddr && !sel.indirect && !isPointer(fn.typeOf(e)) { + if _, eptr := deptr(fn.typeOf(e)); wantAddr && !sel.indirect && !eptr { v = b.addr(fn, e, escaping).address(fn) } else { v = b.expr(fn, e) @@ -968,7 +967,7 @@ func (b *builder) receiver(fn *Function, e ast.Expr, wantAddr, escaping bool, se last := len(sel.index) - 1 // The position of implicit selection is the position of the inducing receiver expression. v = emitImplicitSelections(fn, v, sel.index[:last], e.Pos()) - if !wantAddr && isPointer(v.Type()) { + if _, vptr := deptr(v.Type()); !wantAddr && vptr { v = emitLoad(fn, v) } return v @@ -987,7 +986,7 @@ func (b *builder) setCallFunc(fn *Function, e *ast.CallExpr, c *CallCommon) { obj := sel.obj.(*types.Func) recv := recvType(obj) - wantAddr := isPointer(recv) + _, wantAddr := deptr(recv) escaping := true v := b.receiver(fn, selector.X, wantAddr, escaping, sel) if types.IsInterface(recv) { @@ -1253,36 +1252,13 @@ func (b *builder) arrayLen(fn *Function, elts []ast.Expr) int64 { // literal has type *T behaves like &T{}. // In that case, addr must hold a T, not a *T. func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero bool, sb *storebuf) { - typ := deref(fn.typeOf(e)) // type with name [may be type param] - t := deref(typeparams.CoreType(typ)).Underlying() // core type for comp lit case - // Computing typ and t is subtle as these handle pointer types. - // For example, &T{...} is valid even for maps and slices. - // Also typ should refer to T (not *T) while t should be the core type of T. - // - // To show the ordering to take into account, consider the composite literal - // expressions `&T{f: 1}` and `{f: 1}` within the expression `[]S{{f: 1}}` here: - // type N struct{f int} - // func _[T N, S *N]() { - // _ = &T{f: 1} - // _ = []S{{f: 1}} - // } - // For `&T{f: 1}`, we compute `typ` and `t` as: - // typeOf(&T{f: 1}) == *T - // deref(*T) == T (typ) - // CoreType(T) == N - // deref(N) == N - // N.Underlying() == struct{f int} (t) - // For `{f: 1}` in `[]S{{f: 1}}`, we compute `typ` and `t` as: - // typeOf({f: 1}) == S - // deref(S) == S (typ) - // CoreType(S) == *N - // deref(*N) == N - // N.Underlying() == struct{f int} (t) - switch t := t.(type) { + typ, _ := deref(fn.typeOf(e)) // type with name [may be type param] + switch t := typeparams.CoreType(typ).(type) { case *types.Struct: if !isZero && len(e.Elts) != t.NumFields() { // memclear - sb.store(&address{addr, e.Lbrace, nil}, zeroConst(deref(addr.Type()))) + zt, _ := deref(addr.Type()) + sb.store(&address{addr, e.Lbrace, nil}, zeroConst(zt)) isZero = true } for i, e := range e.Elts { @@ -1326,7 +1302,8 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero if !isZero && int64(len(e.Elts)) != at.Len() { // memclear - sb.store(&address{array, e.Lbrace, nil}, zeroConst(deref(array.Type()))) + zt, _ := deref(array.Type()) + sb.store(&address{array, e.Lbrace, nil}, zeroConst(zt)) } } @@ -1379,8 +1356,13 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero // map[*struct{}]bool{{}: true} // An &-operation may be implied: // map[*struct{}]bool{&struct{}{}: true} + wantAddr := false + if _, ok := unparen(e.Key).(*ast.CompositeLit); ok { + _, wantAddr = deref(t.Key()) + } + var key Value - if _, ok := unparen(e.Key).(*ast.CompositeLit); ok && isPointer(t.Key()) { + if wantAddr { // A CompositeLit never evaluates to a pointer, // so if the type of the location is a pointer, // an &-operation is implied. @@ -1407,7 +1389,7 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero sb.store(&address{addr: addr, pos: e.Lbrace, expr: e}, m) default: - panic("unexpected CompositeLit type: " + t.String()) + panic("unexpected CompositeLit type: " + typ.String()) } } @@ -1873,15 +1855,14 @@ func (b *builder) rangeIndexed(fn *Function, x Value, tv types.Type, pos token.P // Determine number of iterations. var length Value - if arr, ok := deref(x.Type()).Underlying().(*types.Array); ok { + dt, _ := deref(x.Type()) + if arr, ok := typeparams.CoreType(dt).(*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. - // - // TypeParams do not have constant length. Use underlying instead of core type. length = intConst(arr.Len()) } else { // length = len(x). diff --git a/vendor/golang.org/x/tools/go/ssa/doc.go b/vendor/golang.org/x/tools/go/ssa/doc.go index afda476b..a687de45 100644 --- a/vendor/golang.org/x/tools/go/ssa/doc.go +++ b/vendor/golang.org/x/tools/go/ssa/doc.go @@ -66,7 +66,6 @@ // *FieldAddr ✔ ✔ // *FreeVar ✔ // *Function ✔ ✔ (func) -// *GenericConvert ✔ ✔ // *Global ✔ ✔ (var) // *Go ✔ // *If ✔ @@ -80,6 +79,7 @@ // *MakeMap ✔ ✔ // *MakeSlice ✔ ✔ // *MapUpdate ✔ +// *MultiConvert ✔ ✔ // *NamedConst ✔ (const) // *Next ✔ ✔ // *Panic ✔ diff --git a/vendor/golang.org/x/tools/go/ssa/emit.go b/vendor/golang.org/x/tools/go/ssa/emit.go index d3ba3e0c..fe2f6f0f 100644 --- a/vendor/golang.org/x/tools/go/ssa/emit.go +++ b/vendor/golang.org/x/tools/go/ssa/emit.go @@ -11,8 +11,6 @@ import ( "go/ast" "go/token" "go/types" - - "golang.org/x/tools/internal/typeparams" ) // emitNew emits to f a new (heap Alloc) instruction allocating an @@ -29,7 +27,7 @@ func emitNew(f *Function, typ types.Type, pos token.Pos) *Alloc { // new temporary, and returns the value so defined. func emitLoad(f *Function, addr Value) *UnOp { v := &UnOp{Op: token.MUL, X: addr} - v.setType(deref(typeparams.CoreType(addr.Type()))) + v.setType(mustDeref(addr.Type())) f.emit(v) return v } @@ -478,9 +476,8 @@ func emitTailCall(f *Function, call *Call) { // value of a field. func emitImplicitSelections(f *Function, v Value, indices []int, pos token.Pos) Value { for _, index := range indices { - fld := typeparams.CoreType(deref(v.Type())).(*types.Struct).Field(index) - - if isPointer(v.Type()) { + if st, vptr := deref(v.Type()); vptr { + fld := fieldOf(st, index) instr := &FieldAddr{ X: v, Field: index, @@ -489,10 +486,11 @@ func emitImplicitSelections(f *Function, v Value, indices []int, pos token.Pos) instr.setType(types.NewPointer(fld.Type())) v = f.emit(instr) // Load the field's value iff indirectly embedded. - if isPointer(fld.Type()) { + if _, fldptr := deref(fld.Type()); fldptr { v = emitLoad(f, v) } } else { + fld := fieldOf(v.Type(), index) instr := &Field{ X: v, Field: index, @@ -512,8 +510,8 @@ func emitImplicitSelections(f *Function, v Value, indices []int, pos token.Pos) // field's value. // Ident id is used for position and debug info. func emitFieldSelection(f *Function, v Value, index int, wantAddr bool, id *ast.Ident) Value { - fld := typeparams.CoreType(deref(v.Type())).(*types.Struct).Field(index) - if isPointer(v.Type()) { + if st, vptr := deref(v.Type()); vptr { + fld := fieldOf(st, index) instr := &FieldAddr{ X: v, Field: index, @@ -526,6 +524,7 @@ func emitFieldSelection(f *Function, v Value, index int, wantAddr bool, id *ast. v = emitLoad(f, v) } } else { + fld := fieldOf(v.Type(), index) instr := &Field{ X: v, Field: index, diff --git a/vendor/golang.org/x/tools/go/ssa/func.go b/vendor/golang.org/x/tools/go/ssa/func.go index 71f1264e..60cf53f6 100644 --- a/vendor/golang.org/x/tools/go/ssa/func.go +++ b/vendor/golang.org/x/tools/go/ssa/func.go @@ -596,7 +596,7 @@ func WriteFunction(buf *bytes.Buffer, f *Function) { if len(f.Locals) > 0 { buf.WriteString("# Locals:\n") for i, l := range f.Locals { - fmt.Fprintf(buf, "# % 3d:\t%s %s\n", i, l.Name(), relType(deref(l.Type()), from)) + fmt.Fprintf(buf, "# % 3d:\t%s %s\n", i, l.Name(), relType(mustDeref(l.Type()), from)) } } writeSignature(buf, from, f.Name(), f.Signature, f.Params) diff --git a/vendor/golang.org/x/tools/go/ssa/lift.go b/vendor/golang.org/x/tools/go/ssa/lift.go index b9cf7bc3..dbd8790c 100644 --- a/vendor/golang.org/x/tools/go/ssa/lift.go +++ b/vendor/golang.org/x/tools/go/ssa/lift.go @@ -460,7 +460,7 @@ func liftAlloc(df domFrontier, alloc *Alloc, newPhis newPhiMap, fresh *int) bool *fresh++ phi.pos = alloc.Pos() - phi.setType(deref(alloc.Type())) + phi.setType(mustDeref(alloc.Type())) phi.block = v if debugLifting { fmt.Fprintf(os.Stderr, "\tplace %s = %s at block %s\n", phi.Name(), phi, v) @@ -505,7 +505,7 @@ func replaceAll(x, y Value) { func renamed(renaming []Value, alloc *Alloc) Value { v := renaming[alloc.index] if v == nil { - v = zeroConst(deref(alloc.Type())) + v = zeroConst(mustDeref(alloc.Type())) renaming[alloc.index] = v } return v diff --git a/vendor/golang.org/x/tools/go/ssa/methods.go b/vendor/golang.org/x/tools/go/ssa/methods.go index 4185618c..29449837 100644 --- a/vendor/golang.org/x/tools/go/ssa/methods.go +++ b/vendor/golang.org/x/tools/go/ssa/methods.go @@ -101,8 +101,11 @@ func (prog *Program) addMethod(mset *methodSet, sel *types.Selection, cr *creato sel := toSelection(sel) obj := sel.obj.(*types.Func) + _, ptrObj := deptr(recvType(obj)) + _, ptrRecv := deptr(sel.recv) + needsPromotion := len(sel.index) > 1 - needsIndirection := !isPointer(recvType(obj)) && isPointer(sel.recv) + needsIndirection := !ptrObj && ptrRecv if needsPromotion || needsIndirection { fn = makeWrapper(prog, sel, cr) } else { diff --git a/vendor/golang.org/x/tools/go/ssa/print.go b/vendor/golang.org/x/tools/go/ssa/print.go index 8b783196..7f34a7b5 100644 --- a/vendor/golang.org/x/tools/go/ssa/print.go +++ b/vendor/golang.org/x/tools/go/ssa/print.go @@ -95,7 +95,7 @@ func (v *Alloc) String() string { op = "new" } from := v.Parent().relPkg() - return fmt.Sprintf("%s %s (%s)", op, relType(deref(v.Type()), from), v.Comment) + return fmt.Sprintf("%s %s (%s)", op, relType(mustDeref(v.Type()), from), v.Comment) } func (v *Phi) String() string { @@ -259,21 +259,19 @@ func (v *MakeChan) String() string { } func (v *FieldAddr) String() string { - st := typeparams.CoreType(deref(v.X.Type())).(*types.Struct) // Be robust against a bad index. name := "?" - if 0 <= v.Field && v.Field < st.NumFields() { - name = st.Field(v.Field).Name() + if fld := fieldOf(mustDeref(v.X.Type()), v.Field); fld != nil { + name = fld.Name() } return fmt.Sprintf("&%s.%s [#%d]", relName(v.X, v), name, v.Field) } func (v *Field) String() string { - st := typeparams.CoreType(v.X.Type()).(*types.Struct) // Be robust against a bad index. name := "?" - if 0 <= v.Field && v.Field < st.NumFields() { - name = st.Field(v.Field).Name() + if fld := fieldOf(v.X.Type(), v.Field); fld != nil { + name = fld.Name() } return fmt.Sprintf("%s.%s [#%d]", relName(v.X, v), name, v.Field) } @@ -452,7 +450,7 @@ func WritePackage(buf *bytes.Buffer, p *Package) { case *Global: fmt.Fprintf(buf, " var %-*s %s\n", - maxname, name, relType(mem.Type().(*types.Pointer).Elem(), from)) + maxname, name, relType(mustDeref(mem.Type()), from)) } } diff --git a/vendor/golang.org/x/tools/go/ssa/ssa.go b/vendor/golang.org/x/tools/go/ssa/ssa.go index eeb96813..313146d3 100644 --- a/vendor/golang.org/x/tools/go/ssa/ssa.go +++ b/vendor/golang.org/x/tools/go/ssa/ssa.go @@ -865,7 +865,7 @@ type Slice struct { type FieldAddr struct { register X Value // *struct - Field int // field is typeparams.CoreType(X.Type().Underlying().(*types.Pointer).Elem()).(*types.Struct).Field(Field) + Field int // index into CoreType(CoreType(X.Type()).(*types.Pointer).Elem()).(*types.Struct).Fields } // The Field instruction yields the Field of struct X. @@ -884,7 +884,7 @@ type FieldAddr struct { type Field struct { register X Value // struct - Field int // index into typeparams.CoreType(X.Type()).(*types.Struct).Fields + Field int // index into CoreType(X.Type()).(*types.Struct).Fields } // The IndexAddr instruction yields the address of the element at diff --git a/vendor/golang.org/x/tools/go/ssa/subst.go b/vendor/golang.org/x/tools/go/ssa/subst.go index 7efab357..89c41a8d 100644 --- a/vendor/golang.org/x/tools/go/ssa/subst.go +++ b/vendor/golang.org/x/tools/go/ssa/subst.go @@ -249,7 +249,7 @@ func (subst *subster) interface_(iface *types.Interface) *types.Interface { } // methods for the interface. Initially nil if there is no known change needed. - // Signatures for the method where recv is nil. NewInterfaceType fills in the recievers. + // Signatures for the method where recv is nil. NewInterfaceType fills in the receivers. var methods []*types.Func initMethods := func(n int) { // copy first n explicit methods methods = make([]*types.Func, iface.NumExplicitMethods()) @@ -262,7 +262,7 @@ func (subst *subster) interface_(iface *types.Interface) *types.Interface { for i := 0; i < iface.NumExplicitMethods(); i++ { f := iface.ExplicitMethod(i) // On interfaces, we need to cycle break on anonymous interface types - // being in a cycle with their signatures being in cycles with their recievers + // being in a cycle with their signatures being in cycles with their receivers // that do not go through a Named. norecv := changeRecv(f.Type().(*types.Signature), nil) sig := subst.typ(norecv) diff --git a/vendor/golang.org/x/tools/go/ssa/util.go b/vendor/golang.org/x/tools/go/ssa/util.go index e532e1e1..7735dd8e 100644 --- a/vendor/golang.org/x/tools/go/ssa/util.go +++ b/vendor/golang.org/x/tools/go/ssa/util.go @@ -43,12 +43,6 @@ func isBlankIdent(e ast.Expr) bool { //// Type utilities. Some of these belong in go/types. -// isPointer returns true for types whose underlying type is a pointer. -func isPointer(typ types.Type) bool { - _, ok := typ.Underlying().(*types.Pointer) - 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) @@ -100,20 +94,31 @@ func isBasicConvTypes(tset termList) bool { return all && basics >= 1 && tset.Len()-basics <= 1 } -// deref returns a pointer's element type; otherwise it returns typ. -func deref(typ types.Type) types.Type { +// deptr returns a pointer's element type and true; otherwise it returns (typ, false). +// This function is oblivious to core types and is not suitable for generics. +// +// TODO: Deprecate this function once all usages have been audited. +func deptr(typ types.Type) (types.Type, bool) { if p, ok := typ.Underlying().(*types.Pointer); ok { - return p.Elem() + return p.Elem(), true + } + return typ, false +} + +// deref returns the element type of a type with a pointer core type and true; +// otherwise it returns (typ, false). +func deref(typ types.Type) (types.Type, bool) { + if p, ok := typeparams.CoreType(typ).(*types.Pointer); ok { + return p.Elem(), true } - return typ + return typ, false } // mustDeref returns the element type of a type with a pointer core type. // Panics on failure. func mustDeref(typ types.Type) types.Type { - // TODO(taking): Replace deref with mustDeref when possible. - if p, ok := typeparams.CoreType(typ).(*types.Pointer); ok { - return p.Elem() + if et, ok := deref(typ); ok { + return et } panic("cannot dereference type " + typ.String()) } @@ -123,6 +128,17 @@ func recvType(obj *types.Func) types.Type { return obj.Type().(*types.Signature).Recv().Type() } +// fieldOf returns the index'th field of the (core type of) a struct type; +// otherwise returns nil. +func fieldOf(typ types.Type, index int) *types.Var { + if st, ok := typeparams.CoreType(typ).(*types.Struct); ok { + if 0 <= index && index < st.NumFields() { + return st.Field(index) + } + } + return nil +} + // isUntyped returns true for types that are untyped. func isUntyped(typ types.Type) bool { b, ok := typ.(*types.Basic) @@ -182,16 +198,14 @@ func nonbasicTypes(ts []types.Type) []types.Type { return filtered } -// receiverTypeArgs returns the type arguments to a function's reciever. -// Returns an empty list if obj does not have a reciever or its reciever does not have type arguments. +// receiverTypeArgs returns the type arguments to a function's receiver. +// Returns an empty list if obj does not have a receiver or its receiver does not have type arguments. func receiverTypeArgs(obj *types.Func) []types.Type { rtype := recvType(obj) if rtype == nil { return nil } - if isPointer(rtype) { - rtype = rtype.(*types.Pointer).Elem() - } + rtype, _ = deptr(rtype) named, ok := rtype.(*types.Named) if !ok { return nil diff --git a/vendor/golang.org/x/tools/go/ssa/wrappers.go b/vendor/golang.org/x/tools/go/ssa/wrappers.go index 228daf61..123ea685 100644 --- a/vendor/golang.org/x/tools/go/ssa/wrappers.go +++ b/vendor/golang.org/x/tools/go/ssa/wrappers.go @@ -82,12 +82,14 @@ func makeWrapper(prog *Program, sel *selection, cr *creator) *Function { indices := sel.index var v Value = fn.Locals[0] // spilled receiver - if isPointer(sel.recv) { + srdt, ptrRecv := deptr(sel.recv) + if ptrRecv { v = emitLoad(fn, v) // For simple indirection wrappers, perform an informative nil-check: // "value method (T).f called using nil *T pointer" - if len(indices) == 1 && !isPointer(recvType(obj)) { + _, ptrObj := deptr(recvType(obj)) + if len(indices) == 1 && !ptrObj { var c Call c.Call.Value = &Builtin{ name: "ssa:wrapnilchk", @@ -97,7 +99,7 @@ func makeWrapper(prog *Program, sel *selection, cr *creator) *Function { } c.Call.Args = []Value{ v, - stringConst(deref(sel.recv).String()), + stringConst(srdt.String()), stringConst(sel.obj.Name()), } c.setType(v.Type()) @@ -121,7 +123,7 @@ func makeWrapper(prog *Program, sel *selection, cr *creator) *Function { var c Call if r := recvType(obj); !types.IsInterface(r) { // concrete method - if !isPointer(r) { + if _, ptrObj := deptr(r); !ptrObj { v = emitLoad(fn, v) } callee := prog.originFunc(obj) diff --git a/vendor/golang.org/x/tools/internal/event/tag/tag.go b/vendor/golang.org/x/tools/internal/event/tag/tag.go new file mode 100644 index 00000000..ff2f2ecd --- /dev/null +++ b/vendor/golang.org/x/tools/internal/event/tag/tag.go @@ -0,0 +1,59 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package tag provides the labels used for telemetry throughout gopls. +package tag + +import ( + "golang.org/x/tools/internal/event/keys" +) + +var ( + // create the label keys we use + Method = keys.NewString("method", "") + StatusCode = keys.NewString("status.code", "") + StatusMessage = keys.NewString("status.message", "") + RPCID = keys.NewString("id", "") + RPCDirection = keys.NewString("direction", "") + File = keys.NewString("file", "") + Directory = keys.New("directory", "") + URI = keys.New("URI", "") + Package = keys.NewString("package", "") // Package ID + PackagePath = keys.NewString("package_path", "") + Query = keys.New("query", "") + Snapshot = keys.NewUInt64("snapshot", "") + Operation = keys.NewString("operation", "") + + Position = keys.New("position", "") + Category = keys.NewString("category", "") + PackageCount = keys.NewInt("packages", "") + Files = keys.New("files", "") + Port = keys.NewInt("port", "") + Type = keys.New("type", "") + HoverKind = keys.NewString("hoverkind", "") + + NewServer = keys.NewString("new_server", "A new server was added") + EndServer = keys.NewString("end_server", "A server was shut down") + + ServerID = keys.NewString("server", "The server ID an event is related to") + Logfile = keys.NewString("logfile", "") + DebugAddress = keys.NewString("debug_address", "") + GoplsPath = keys.NewString("gopls_path", "") + ClientID = keys.NewString("client_id", "") + + Level = keys.NewInt("level", "The logging level") +) + +var ( + // create the stats we measure + Started = keys.NewInt64("started", "Count of started RPCs.") + ReceivedBytes = keys.NewInt64("received_bytes", "Bytes received.") //, unit.Bytes) + SentBytes = keys.NewInt64("sent_bytes", "Bytes sent.") //, unit.Bytes) + Latency = keys.NewFloat64("latency_ms", "Elapsed time in milliseconds") //, unit.Milliseconds) +) + +const ( + Inbound = "in" + Outbound = "out" +) diff --git a/vendor/golang.org/x/tools/internal/gcimporter/bexport.go b/vendor/golang.org/x/tools/internal/gcimporter/bexport.go deleted file mode 100644 index 30582ed6..00000000 --- a/vendor/golang.org/x/tools/internal/gcimporter/bexport.go +++ /dev/null @@ -1,852 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Binary package export. -// This file was derived from $GOROOT/src/cmd/compile/internal/gc/bexport.go; -// see that file for specification of the format. - -package gcimporter - -import ( - "bytes" - "encoding/binary" - "fmt" - "go/constant" - "go/token" - "go/types" - "math" - "math/big" - "sort" - "strings" -) - -// If debugFormat is set, each integer and string value is preceded by a marker -// and position information in the encoding. This mechanism permits an importer -// to recognize immediately when it is out of sync. The importer recognizes this -// mode automatically (i.e., it can import export data produced with debugging -// support even if debugFormat is not set at the time of import). This mode will -// lead to massively larger export data (by a factor of 2 to 3) and should only -// be enabled during development and debugging. -// -// NOTE: This flag is the first flag to enable if importing dies because of -// (suspected) format errors, and whenever a change is made to the format. -const debugFormat = false // default: false - -// Current export format version. Increase with each format change. -// -// Note: The latest binary (non-indexed) export format is at version 6. -// This exporter is still at level 4, but it doesn't matter since -// the binary importer can handle older versions just fine. -// -// 6: package height (CL 105038) -- NOT IMPLEMENTED HERE -// 5: improved position encoding efficiency (issue 20080, CL 41619) -- NOT IMPLEMENTED HERE -// 4: type name objects support type aliases, uses aliasTag -// 3: Go1.8 encoding (same as version 2, aliasTag defined but never used) -// 2: removed unused bool in ODCL export (compiler only) -// 1: header format change (more regular), export package for _ struct fields -// 0: Go1.7 encoding -const exportVersion = 4 - -// trackAllTypes enables cycle tracking for all types, not just named -// types. The existing compiler invariants assume that unnamed types -// that are not completely set up are not used, or else there are spurious -// errors. -// If disabled, only named types are tracked, possibly leading to slightly -// less efficient encoding in rare cases. It also prevents the export of -// some corner-case type declarations (but those are not handled correctly -// with with the textual export format either). -// TODO(gri) enable and remove once issues caused by it are fixed -const trackAllTypes = false - -type exporter struct { - fset *token.FileSet - out bytes.Buffer - - // object -> index maps, indexed in order of serialization - strIndex map[string]int - pkgIndex map[*types.Package]int - typIndex map[types.Type]int - - // position encoding - posInfoFormat bool - prevFile string - prevLine int - - // debugging support - written int // bytes written - indent int // for trace -} - -// internalError represents an error generated inside this package. -type internalError string - -func (e internalError) Error() string { return "gcimporter: " + string(e) } - -func internalErrorf(format string, args ...interface{}) error { - return internalError(fmt.Sprintf(format, args...)) -} - -// BExportData returns binary export data for pkg. -// If no file set is provided, position info will be missing. -func BExportData(fset *token.FileSet, pkg *types.Package) (b []byte, err error) { - if !debug { - defer func() { - if e := recover(); e != nil { - if ierr, ok := e.(internalError); ok { - err = ierr - return - } - // Not an internal error; panic again. - panic(e) - } - }() - } - - p := exporter{ - fset: fset, - strIndex: map[string]int{"": 0}, // empty string is mapped to 0 - pkgIndex: make(map[*types.Package]int), - typIndex: make(map[types.Type]int), - posInfoFormat: true, // TODO(gri) might become a flag, eventually - } - - // write version info - // The version string must start with "version %d" where %d is the version - // number. Additional debugging information may follow after a blank; that - // text is ignored by the importer. - p.rawStringln(fmt.Sprintf("version %d", exportVersion)) - var debug string - if debugFormat { - debug = "debug" - } - p.rawStringln(debug) // cannot use p.bool since it's affected by debugFormat; also want to see this clearly - p.bool(trackAllTypes) - p.bool(p.posInfoFormat) - - // --- generic export data --- - - // populate type map with predeclared "known" types - for index, typ := range predeclared() { - p.typIndex[typ] = index - } - if len(p.typIndex) != len(predeclared()) { - return nil, internalError("duplicate entries in type map?") - } - - // write package data - p.pkg(pkg, true) - if trace { - p.tracef("\n") - } - - // write objects - objcount := 0 - scope := pkg.Scope() - for _, name := range scope.Names() { - if !token.IsExported(name) { - continue - } - if trace { - p.tracef("\n") - } - p.obj(scope.Lookup(name)) - objcount++ - } - - // indicate end of list - if trace { - p.tracef("\n") - } - p.tag(endTag) - - // for self-verification only (redundant) - p.int(objcount) - - if trace { - p.tracef("\n") - } - - // --- end of export data --- - - return p.out.Bytes(), nil -} - -func (p *exporter) pkg(pkg *types.Package, emptypath bool) { - if pkg == nil { - panic(internalError("unexpected nil pkg")) - } - - // if we saw the package before, write its index (>= 0) - if i, ok := p.pkgIndex[pkg]; ok { - p.index('P', i) - return - } - - // otherwise, remember the package, write the package tag (< 0) and package data - if trace { - p.tracef("P%d = { ", len(p.pkgIndex)) - defer p.tracef("} ") - } - p.pkgIndex[pkg] = len(p.pkgIndex) - - p.tag(packageTag) - p.string(pkg.Name()) - if emptypath { - p.string("") - } else { - p.string(pkg.Path()) - } -} - -func (p *exporter) obj(obj types.Object) { - switch obj := obj.(type) { - case *types.Const: - p.tag(constTag) - p.pos(obj) - p.qualifiedName(obj) - p.typ(obj.Type()) - p.value(obj.Val()) - - case *types.TypeName: - if obj.IsAlias() { - p.tag(aliasTag) - p.pos(obj) - p.qualifiedName(obj) - } else { - p.tag(typeTag) - } - p.typ(obj.Type()) - - case *types.Var: - p.tag(varTag) - p.pos(obj) - p.qualifiedName(obj) - p.typ(obj.Type()) - - case *types.Func: - p.tag(funcTag) - p.pos(obj) - p.qualifiedName(obj) - sig := obj.Type().(*types.Signature) - p.paramList(sig.Params(), sig.Variadic()) - p.paramList(sig.Results(), false) - - default: - panic(internalErrorf("unexpected object %v (%T)", obj, obj)) - } -} - -func (p *exporter) pos(obj types.Object) { - if !p.posInfoFormat { - return - } - - file, line := p.fileLine(obj) - if file == p.prevFile { - // common case: write line delta - // delta == 0 means different file or no line change - delta := line - p.prevLine - p.int(delta) - if delta == 0 { - p.int(-1) // -1 means no file change - } - } else { - // different file - p.int(0) - // Encode filename as length of common prefix with previous - // filename, followed by (possibly empty) suffix. Filenames - // frequently share path prefixes, so this can save a lot - // of space and make export data size less dependent on file - // path length. The suffix is unlikely to be empty because - // file names tend to end in ".go". - n := commonPrefixLen(p.prevFile, file) - p.int(n) // n >= 0 - p.string(file[n:]) // write suffix only - p.prevFile = file - p.int(line) - } - p.prevLine = line -} - -func (p *exporter) fileLine(obj types.Object) (file string, line int) { - if p.fset != nil { - pos := p.fset.Position(obj.Pos()) - file = pos.Filename - line = pos.Line - } - return -} - -func commonPrefixLen(a, b string) int { - if len(a) > len(b) { - a, b = b, a - } - // len(a) <= len(b) - i := 0 - for i < len(a) && a[i] == b[i] { - i++ - } - return i -} - -func (p *exporter) qualifiedName(obj types.Object) { - p.string(obj.Name()) - p.pkg(obj.Pkg(), false) -} - -func (p *exporter) typ(t types.Type) { - if t == nil { - panic(internalError("nil type")) - } - - // Possible optimization: Anonymous pointer types *T where - // T is a named type are common. We could canonicalize all - // such types *T to a single type PT = *T. This would lead - // to at most one *T entry in typIndex, and all future *T's - // would be encoded as the respective index directly. Would - // save 1 byte (pointerTag) per *T and reduce the typIndex - // size (at the cost of a canonicalization map). We can do - // this later, without encoding format change. - - // if we saw the type before, write its index (>= 0) - if i, ok := p.typIndex[t]; ok { - p.index('T', i) - return - } - - // otherwise, remember the type, write the type tag (< 0) and type data - if trackAllTypes { - if trace { - p.tracef("T%d = {>\n", len(p.typIndex)) - defer p.tracef("<\n} ") - } - p.typIndex[t] = len(p.typIndex) - } - - switch t := t.(type) { - case *types.Named: - if !trackAllTypes { - // if we don't track all types, track named types now - p.typIndex[t] = len(p.typIndex) - } - - p.tag(namedTag) - p.pos(t.Obj()) - p.qualifiedName(t.Obj()) - p.typ(t.Underlying()) - if !types.IsInterface(t) { - p.assocMethods(t) - } - - case *types.Array: - p.tag(arrayTag) - p.int64(t.Len()) - p.typ(t.Elem()) - - case *types.Slice: - p.tag(sliceTag) - p.typ(t.Elem()) - - case *dddSlice: - p.tag(dddTag) - p.typ(t.elem) - - case *types.Struct: - p.tag(structTag) - p.fieldList(t) - - case *types.Pointer: - p.tag(pointerTag) - p.typ(t.Elem()) - - case *types.Signature: - p.tag(signatureTag) - p.paramList(t.Params(), t.Variadic()) - p.paramList(t.Results(), false) - - case *types.Interface: - p.tag(interfaceTag) - p.iface(t) - - case *types.Map: - p.tag(mapTag) - p.typ(t.Key()) - p.typ(t.Elem()) - - case *types.Chan: - p.tag(chanTag) - p.int(int(3 - t.Dir())) // hack - p.typ(t.Elem()) - - default: - panic(internalErrorf("unexpected type %T: %s", t, t)) - } -} - -func (p *exporter) assocMethods(named *types.Named) { - // Sort methods (for determinism). - var methods []*types.Func - for i := 0; i < named.NumMethods(); i++ { - methods = append(methods, named.Method(i)) - } - sort.Sort(methodsByName(methods)) - - p.int(len(methods)) - - if trace && methods != nil { - p.tracef("associated methods {>\n") - } - - for i, m := range methods { - if trace && i > 0 { - p.tracef("\n") - } - - p.pos(m) - name := m.Name() - p.string(name) - if !exported(name) { - p.pkg(m.Pkg(), false) - } - - sig := m.Type().(*types.Signature) - p.paramList(types.NewTuple(sig.Recv()), false) - p.paramList(sig.Params(), sig.Variadic()) - p.paramList(sig.Results(), false) - p.int(0) // dummy value for go:nointerface pragma - ignored by importer - } - - if trace && methods != nil { - p.tracef("<\n} ") - } -} - -type methodsByName []*types.Func - -func (x methodsByName) Len() int { return len(x) } -func (x methodsByName) Swap(i, j int) { x[i], x[j] = x[j], x[i] } -func (x methodsByName) Less(i, j int) bool { return x[i].Name() < x[j].Name() } - -func (p *exporter) fieldList(t *types.Struct) { - if trace && t.NumFields() > 0 { - p.tracef("fields {>\n") - defer p.tracef("<\n} ") - } - - p.int(t.NumFields()) - for i := 0; i < t.NumFields(); i++ { - if trace && i > 0 { - p.tracef("\n") - } - p.field(t.Field(i)) - p.string(t.Tag(i)) - } -} - -func (p *exporter) field(f *types.Var) { - if !f.IsField() { - panic(internalError("field expected")) - } - - p.pos(f) - p.fieldName(f) - p.typ(f.Type()) -} - -func (p *exporter) iface(t *types.Interface) { - // TODO(gri): enable importer to load embedded interfaces, - // then emit Embeddeds and ExplicitMethods separately here. - p.int(0) - - n := t.NumMethods() - if trace && n > 0 { - p.tracef("methods {>\n") - defer p.tracef("<\n} ") - } - p.int(n) - for i := 0; i < n; i++ { - if trace && i > 0 { - p.tracef("\n") - } - p.method(t.Method(i)) - } -} - -func (p *exporter) method(m *types.Func) { - sig := m.Type().(*types.Signature) - if sig.Recv() == nil { - panic(internalError("method expected")) - } - - p.pos(m) - p.string(m.Name()) - if m.Name() != "_" && !token.IsExported(m.Name()) { - p.pkg(m.Pkg(), false) - } - - // interface method; no need to encode receiver. - p.paramList(sig.Params(), sig.Variadic()) - p.paramList(sig.Results(), false) -} - -func (p *exporter) fieldName(f *types.Var) { - name := f.Name() - - if f.Anonymous() { - // anonymous field - we distinguish between 3 cases: - // 1) field name matches base type name and is exported - // 2) field name matches base type name and is not exported - // 3) field name doesn't match base type name (alias name) - bname := basetypeName(f.Type()) - if name == bname { - if token.IsExported(name) { - name = "" // 1) we don't need to know the field name or package - } else { - name = "?" // 2) use unexported name "?" to force package export - } - } else { - // 3) indicate alias and export name as is - // (this requires an extra "@" but this is a rare case) - p.string("@") - } - } - - p.string(name) - if name != "" && !token.IsExported(name) { - p.pkg(f.Pkg(), false) - } -} - -func basetypeName(typ types.Type) string { - switch typ := deref(typ).(type) { - case *types.Basic: - return typ.Name() - case *types.Named: - return typ.Obj().Name() - default: - return "" // unnamed type - } -} - -func (p *exporter) paramList(params *types.Tuple, variadic bool) { - // use negative length to indicate unnamed parameters - // (look at the first parameter only since either all - // names are present or all are absent) - n := params.Len() - if n > 0 && params.At(0).Name() == "" { - n = -n - } - p.int(n) - for i := 0; i < params.Len(); i++ { - q := params.At(i) - t := q.Type() - if variadic && i == params.Len()-1 { - t = &dddSlice{t.(*types.Slice).Elem()} - } - p.typ(t) - if n > 0 { - name := q.Name() - p.string(name) - if name != "_" { - p.pkg(q.Pkg(), false) - } - } - p.string("") // no compiler-specific info - } -} - -func (p *exporter) value(x constant.Value) { - if trace { - p.tracef("= ") - } - - switch x.Kind() { - case constant.Bool: - tag := falseTag - if constant.BoolVal(x) { - tag = trueTag - } - p.tag(tag) - - case constant.Int: - if v, exact := constant.Int64Val(x); exact { - // common case: x fits into an int64 - use compact encoding - p.tag(int64Tag) - p.int64(v) - return - } - // uncommon case: large x - use float encoding - // (powers of 2 will be encoded efficiently with exponent) - p.tag(floatTag) - p.float(constant.ToFloat(x)) - - case constant.Float: - p.tag(floatTag) - p.float(x) - - case constant.Complex: - p.tag(complexTag) - p.float(constant.Real(x)) - p.float(constant.Imag(x)) - - case constant.String: - p.tag(stringTag) - p.string(constant.StringVal(x)) - - case constant.Unknown: - // package contains type errors - p.tag(unknownTag) - - default: - panic(internalErrorf("unexpected value %v (%T)", x, x)) - } -} - -func (p *exporter) float(x constant.Value) { - if x.Kind() != constant.Float { - panic(internalErrorf("unexpected constant %v, want float", x)) - } - // extract sign (there is no -0) - sign := constant.Sign(x) - if sign == 0 { - // x == 0 - p.int(0) - return - } - // x != 0 - - var f big.Float - if v, exact := constant.Float64Val(x); exact { - // float64 - f.SetFloat64(v) - } else if num, denom := constant.Num(x), constant.Denom(x); num.Kind() == constant.Int { - // TODO(gri): add big.Rat accessor to constant.Value. - r := valueToRat(num) - f.SetRat(r.Quo(r, valueToRat(denom))) - } else { - // Value too large to represent as a fraction => inaccessible. - // TODO(gri): add big.Float accessor to constant.Value. - f.SetFloat64(math.MaxFloat64) // FIXME - } - - // extract exponent such that 0.5 <= m < 1.0 - var m big.Float - exp := f.MantExp(&m) - - // extract mantissa as *big.Int - // - set exponent large enough so mant satisfies mant.IsInt() - // - get *big.Int from mant - m.SetMantExp(&m, int(m.MinPrec())) - mant, acc := m.Int(nil) - if acc != big.Exact { - panic(internalError("internal error")) - } - - p.int(sign) - p.int(exp) - p.string(string(mant.Bytes())) -} - -func valueToRat(x constant.Value) *big.Rat { - // Convert little-endian to big-endian. - // I can't believe this is necessary. - bytes := constant.Bytes(x) - for i := 0; i < len(bytes)/2; i++ { - bytes[i], bytes[len(bytes)-1-i] = bytes[len(bytes)-1-i], bytes[i] - } - return new(big.Rat).SetInt(new(big.Int).SetBytes(bytes)) -} - -func (p *exporter) bool(b bool) bool { - if trace { - p.tracef("[") - defer p.tracef("= %v] ", b) - } - - x := 0 - if b { - x = 1 - } - p.int(x) - return b -} - -// ---------------------------------------------------------------------------- -// Low-level encoders - -func (p *exporter) index(marker byte, index int) { - if index < 0 { - panic(internalError("invalid index < 0")) - } - if debugFormat { - p.marker('t') - } - if trace { - p.tracef("%c%d ", marker, index) - } - p.rawInt64(int64(index)) -} - -func (p *exporter) tag(tag int) { - if tag >= 0 { - panic(internalError("invalid tag >= 0")) - } - if debugFormat { - p.marker('t') - } - if trace { - p.tracef("%s ", tagString[-tag]) - } - p.rawInt64(int64(tag)) -} - -func (p *exporter) int(x int) { - p.int64(int64(x)) -} - -func (p *exporter) int64(x int64) { - if debugFormat { - p.marker('i') - } - if trace { - p.tracef("%d ", x) - } - p.rawInt64(x) -} - -func (p *exporter) string(s string) { - if debugFormat { - p.marker('s') - } - if trace { - p.tracef("%q ", s) - } - // if we saw the string before, write its index (>= 0) - // (the empty string is mapped to 0) - if i, ok := p.strIndex[s]; ok { - p.rawInt64(int64(i)) - return - } - // otherwise, remember string and write its negative length and bytes - p.strIndex[s] = len(p.strIndex) - p.rawInt64(-int64(len(s))) - for i := 0; i < len(s); i++ { - p.rawByte(s[i]) - } -} - -// marker emits a marker byte and position information which makes -// it easy for a reader to detect if it is "out of sync". Used for -// debugFormat format only. -func (p *exporter) marker(m byte) { - p.rawByte(m) - // Enable this for help tracking down the location - // of an incorrect marker when running in debugFormat. - if false && trace { - p.tracef("#%d ", p.written) - } - p.rawInt64(int64(p.written)) -} - -// rawInt64 should only be used by low-level encoders. -func (p *exporter) rawInt64(x int64) { - var tmp [binary.MaxVarintLen64]byte - n := binary.PutVarint(tmp[:], x) - for i := 0; i < n; i++ { - p.rawByte(tmp[i]) - } -} - -// rawStringln should only be used to emit the initial version string. -func (p *exporter) rawStringln(s string) { - for i := 0; i < len(s); i++ { - p.rawByte(s[i]) - } - p.rawByte('\n') -} - -// rawByte is the bottleneck interface to write to p.out. -// rawByte escapes b as follows (any encoding does that -// hides '$'): -// -// '$' => '|' 'S' -// '|' => '|' '|' -// -// Necessary so other tools can find the end of the -// export data by searching for "$$". -// rawByte should only be used by low-level encoders. -func (p *exporter) rawByte(b byte) { - switch b { - case '$': - // write '$' as '|' 'S' - b = 'S' - fallthrough - case '|': - // write '|' as '|' '|' - p.out.WriteByte('|') - p.written++ - } - p.out.WriteByte(b) - p.written++ -} - -// tracef is like fmt.Printf but it rewrites the format string -// to take care of indentation. -func (p *exporter) tracef(format string, args ...interface{}) { - if strings.ContainsAny(format, "<>\n") { - var buf bytes.Buffer - for i := 0; i < len(format); i++ { - // no need to deal with runes - ch := format[i] - switch ch { - case '>': - p.indent++ - continue - case '<': - p.indent-- - continue - } - buf.WriteByte(ch) - if ch == '\n' { - for j := p.indent; j > 0; j-- { - buf.WriteString(". ") - } - } - } - format = buf.String() - } - fmt.Printf(format, args...) -} - -// Debugging support. -// (tagString is only used when tracing is enabled) -var tagString = [...]string{ - // Packages - -packageTag: "package", - - // Types - -namedTag: "named type", - -arrayTag: "array", - -sliceTag: "slice", - -dddTag: "ddd", - -structTag: "struct", - -pointerTag: "pointer", - -signatureTag: "signature", - -interfaceTag: "interface", - -mapTag: "map", - -chanTag: "chan", - - // Values - -falseTag: "false", - -trueTag: "true", - -int64Tag: "int64", - -floatTag: "float", - -fractionTag: "fraction", - -complexTag: "complex", - -stringTag: "string", - -unknownTag: "unknown", - - // Type aliases - -aliasTag: "alias", -} diff --git a/vendor/golang.org/x/tools/internal/gcimporter/bimport.go b/vendor/golang.org/x/tools/internal/gcimporter/bimport.go index b85de014..d98b0db2 100644 --- a/vendor/golang.org/x/tools/internal/gcimporter/bimport.go +++ b/vendor/golang.org/x/tools/internal/gcimporter/bimport.go @@ -2,340 +2,24 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// This file is a copy of $GOROOT/src/go/internal/gcimporter/bimport.go. +// This file contains the remaining vestiges of +// $GOROOT/src/go/internal/gcimporter/bimport.go. package gcimporter import ( - "encoding/binary" "fmt" - "go/constant" "go/token" "go/types" - "sort" - "strconv" - "strings" "sync" - "unicode" - "unicode/utf8" ) -type importer struct { - imports map[string]*types.Package - data []byte - importpath string - buf []byte // for reading strings - version int // export format version - - // object lists - strList []string // in order of appearance - pathList []string // in order of appearance - pkgList []*types.Package // in order of appearance - typList []types.Type // in order of appearance - interfaceList []*types.Interface // for delayed completion only - trackAllTypes bool - - // position encoding - posInfoFormat bool - prevFile string - prevLine int - fake fakeFileSet - - // debugging support - debugFormat bool - read int // bytes read -} - -// BImportData imports a package from the serialized package data -// and returns the number of bytes consumed and a reference to the package. -// If the export data version is not recognized or the format is otherwise -// compromised, an error is returned. -func BImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) { - // catch panics and return them as errors - const currentVersion = 6 - version := -1 // unknown version - defer func() { - if e := recover(); e != nil { - // Return a (possibly nil or incomplete) package unchanged (see #16088). - if version > currentVersion { - err = fmt.Errorf("cannot import %q (%v), export data is newer version - update tool", path, e) - } else { - err = fmt.Errorf("cannot import %q (%v), possibly version skew - reinstall package", path, e) - } - } - }() - - p := importer{ - imports: imports, - data: data, - importpath: path, - version: version, - strList: []string{""}, // empty string is mapped to 0 - pathList: []string{""}, // empty string is mapped to 0 - fake: fakeFileSet{ - fset: fset, - files: make(map[string]*fileInfo), - }, - } - defer p.fake.setLines() // set lines for files in fset - - // read version info - var versionstr string - if b := p.rawByte(); b == 'c' || b == 'd' { - // Go1.7 encoding; first byte encodes low-level - // encoding format (compact vs debug). - // For backward-compatibility only (avoid problems with - // old installed packages). Newly compiled packages use - // the extensible format string. - // TODO(gri) Remove this support eventually; after Go1.8. - if b == 'd' { - p.debugFormat = true - } - p.trackAllTypes = p.rawByte() == 'a' - p.posInfoFormat = p.int() != 0 - versionstr = p.string() - if versionstr == "v1" { - version = 0 - } - } else { - // Go1.8 extensible encoding - // read version string and extract version number (ignore anything after the version number) - versionstr = p.rawStringln(b) - if s := strings.SplitN(versionstr, " ", 3); len(s) >= 2 && s[0] == "version" { - if v, err := strconv.Atoi(s[1]); err == nil && v > 0 { - version = v - } - } - } - p.version = version - - // read version specific flags - extend as necessary - switch p.version { - // case currentVersion: - // ... - // fallthrough - case currentVersion, 5, 4, 3, 2, 1: - p.debugFormat = p.rawStringln(p.rawByte()) == "debug" - p.trackAllTypes = p.int() != 0 - p.posInfoFormat = p.int() != 0 - case 0: - // Go1.7 encoding format - nothing to do here - default: - errorf("unknown bexport format version %d (%q)", p.version, versionstr) - } - - // --- generic export data --- - - // populate typList with predeclared "known" types - p.typList = append(p.typList, predeclared()...) - - // read package data - pkg = p.pkg() - - // read objects of phase 1 only (see cmd/compile/internal/gc/bexport.go) - objcount := 0 - for { - tag := p.tagOrIndex() - if tag == endTag { - break - } - p.obj(tag) - objcount++ - } - - // self-verification - if count := p.int(); count != objcount { - errorf("got %d objects; want %d", objcount, count) - } - - // ignore compiler-specific import data - - // complete interfaces - // TODO(gri) re-investigate if we still need to do this in a delayed fashion - for _, typ := range p.interfaceList { - typ.Complete() - } - - // record all referenced packages as imports - list := append(([]*types.Package)(nil), p.pkgList[1:]...) - sort.Sort(byPath(list)) - pkg.SetImports(list) - - // package was imported completely and without errors - pkg.MarkComplete() - - return p.read, pkg, nil -} - func errorf(format string, args ...interface{}) { panic(fmt.Sprintf(format, args...)) } -func (p *importer) pkg() *types.Package { - // if the package was seen before, i is its index (>= 0) - i := p.tagOrIndex() - if i >= 0 { - return p.pkgList[i] - } - - // otherwise, i is the package tag (< 0) - if i != packageTag { - errorf("unexpected package tag %d version %d", i, p.version) - } - - // read package data - name := p.string() - var path string - if p.version >= 5 { - path = p.path() - } else { - path = p.string() - } - if p.version >= 6 { - p.int() // package height; unused by go/types - } - - // we should never see an empty package name - if name == "" { - errorf("empty package name in import") - } - - // an empty path denotes the package we are currently importing; - // it must be the first package we see - if (path == "") != (len(p.pkgList) == 0) { - errorf("package path %q for pkg index %d", path, len(p.pkgList)) - } - - // if the package was imported before, use that one; otherwise create a new one - if path == "" { - path = p.importpath - } - pkg := p.imports[path] - if pkg == nil { - pkg = types.NewPackage(path, name) - p.imports[path] = pkg - } else if pkg.Name() != name { - errorf("conflicting names %s and %s for package %q", pkg.Name(), name, path) - } - p.pkgList = append(p.pkgList, pkg) - - return pkg -} - -// objTag returns the tag value for each object kind. -func objTag(obj types.Object) int { - switch obj.(type) { - case *types.Const: - return constTag - case *types.TypeName: - return typeTag - case *types.Var: - return varTag - case *types.Func: - return funcTag - default: - errorf("unexpected object: %v (%T)", obj, obj) // panics - panic("unreachable") - } -} - -func sameObj(a, b types.Object) bool { - // Because unnamed types are not canonicalized, we cannot simply compare types for - // (pointer) identity. - // Ideally we'd check equality of constant values as well, but this is good enough. - return objTag(a) == objTag(b) && types.Identical(a.Type(), b.Type()) -} - -func (p *importer) declare(obj types.Object) { - pkg := obj.Pkg() - if alt := pkg.Scope().Insert(obj); alt != nil { - // This can only trigger if we import a (non-type) object a second time. - // Excluding type aliases, this cannot happen because 1) we only import a package - // once; and b) we ignore compiler-specific export data which may contain - // functions whose inlined function bodies refer to other functions that - // were already imported. - // However, type aliases require reexporting the original type, so we need - // to allow it (see also the comment in cmd/compile/internal/gc/bimport.go, - // method importer.obj, switch case importing functions). - // TODO(gri) review/update this comment once the gc compiler handles type aliases. - if !sameObj(obj, alt) { - errorf("inconsistent import:\n\t%v\npreviously imported as:\n\t%v\n", obj, alt) - } - } -} - -func (p *importer) obj(tag int) { - switch tag { - case constTag: - pos := p.pos() - pkg, name := p.qualifiedName() - typ := p.typ(nil, nil) - val := p.value() - p.declare(types.NewConst(pos, pkg, name, typ, val)) - - case aliasTag: - // TODO(gri) verify type alias hookup is correct - pos := p.pos() - pkg, name := p.qualifiedName() - typ := p.typ(nil, nil) - p.declare(types.NewTypeName(pos, pkg, name, typ)) - - case typeTag: - p.typ(nil, nil) - - case varTag: - pos := p.pos() - pkg, name := p.qualifiedName() - typ := p.typ(nil, nil) - p.declare(types.NewVar(pos, pkg, name, typ)) - - case funcTag: - pos := p.pos() - pkg, name := p.qualifiedName() - params, isddd := p.paramList() - result, _ := p.paramList() - sig := types.NewSignature(nil, params, result, isddd) - p.declare(types.NewFunc(pos, pkg, name, sig)) - - default: - errorf("unexpected object tag %d", tag) - } -} - const deltaNewFile = -64 // see cmd/compile/internal/gc/bexport.go -func (p *importer) pos() token.Pos { - if !p.posInfoFormat { - return token.NoPos - } - - file := p.prevFile - line := p.prevLine - delta := p.int() - line += delta - if p.version >= 5 { - if delta == deltaNewFile { - if n := p.int(); n >= 0 { - // file changed - file = p.path() - line = n - } - } - } else { - if delta == 0 { - if n := p.int(); n >= 0 { - // file changed - file = p.prevFile[:n] + p.string() - line = p.int() - } - } - } - p.prevFile = file - p.prevLine = line - - return p.fake.pos(file, line, 0) -} - // Synthesize a token.Pos type fakeFileSet struct { fset *token.FileSet @@ -389,205 +73,6 @@ var ( fakeLinesOnce sync.Once ) -func (p *importer) qualifiedName() (pkg *types.Package, name string) { - name = p.string() - pkg = p.pkg() - return -} - -func (p *importer) record(t types.Type) { - p.typList = append(p.typList, t) -} - -// A dddSlice is a types.Type representing ...T parameters. -// It only appears for parameter types and does not escape -// the importer. -type dddSlice struct { - elem types.Type -} - -func (t *dddSlice) Underlying() types.Type { return t } -func (t *dddSlice) String() string { return "..." + t.elem.String() } - -// parent is the package which declared the type; parent == nil means -// the package currently imported. The parent package is needed for -// exported struct fields and interface methods which don't contain -// explicit package information in the export data. -// -// A non-nil tname is used as the "owner" of the result type; i.e., -// the result type is the underlying type of tname. tname is used -// to give interface methods a named receiver type where possible. -func (p *importer) typ(parent *types.Package, tname *types.Named) types.Type { - // if the type was seen before, i is its index (>= 0) - i := p.tagOrIndex() - if i >= 0 { - return p.typList[i] - } - - // otherwise, i is the type tag (< 0) - switch i { - case namedTag: - // read type object - pos := p.pos() - parent, name := p.qualifiedName() - scope := parent.Scope() - obj := scope.Lookup(name) - - // if the object doesn't exist yet, create and insert it - if obj == nil { - obj = types.NewTypeName(pos, parent, name, nil) - scope.Insert(obj) - } - - if _, ok := obj.(*types.TypeName); !ok { - errorf("pkg = %s, name = %s => %s", parent, name, obj) - } - - // associate new named type with obj if it doesn't exist yet - t0 := types.NewNamed(obj.(*types.TypeName), nil, nil) - - // but record the existing type, if any - tname := obj.Type().(*types.Named) // tname is either t0 or the existing type - p.record(tname) - - // read underlying type - t0.SetUnderlying(p.typ(parent, t0)) - - // interfaces don't have associated methods - if types.IsInterface(t0) { - return tname - } - - // read associated methods - for i := p.int(); i > 0; i-- { - // TODO(gri) replace this with something closer to fieldName - pos := p.pos() - name := p.string() - if !exported(name) { - p.pkg() - } - - recv, _ := p.paramList() // TODO(gri) do we need a full param list for the receiver? - params, isddd := p.paramList() - result, _ := p.paramList() - p.int() // go:nointerface pragma - discarded - - sig := types.NewSignature(recv.At(0), params, result, isddd) - t0.AddMethod(types.NewFunc(pos, parent, name, sig)) - } - - return tname - - case arrayTag: - t := new(types.Array) - if p.trackAllTypes { - p.record(t) - } - - n := p.int64() - *t = *types.NewArray(p.typ(parent, nil), n) - return t - - case sliceTag: - t := new(types.Slice) - if p.trackAllTypes { - p.record(t) - } - - *t = *types.NewSlice(p.typ(parent, nil)) - return t - - case dddTag: - t := new(dddSlice) - if p.trackAllTypes { - p.record(t) - } - - t.elem = p.typ(parent, nil) - return t - - case structTag: - t := new(types.Struct) - if p.trackAllTypes { - p.record(t) - } - - *t = *types.NewStruct(p.fieldList(parent)) - return t - - case pointerTag: - t := new(types.Pointer) - if p.trackAllTypes { - p.record(t) - } - - *t = *types.NewPointer(p.typ(parent, nil)) - return t - - case signatureTag: - t := new(types.Signature) - if p.trackAllTypes { - p.record(t) - } - - params, isddd := p.paramList() - result, _ := p.paramList() - *t = *types.NewSignature(nil, params, result, isddd) - return t - - case interfaceTag: - // Create a dummy entry in the type list. This is safe because we - // cannot expect the interface type to appear in a cycle, as any - // such cycle must contain a named type which would have been - // first defined earlier. - // TODO(gri) Is this still true now that we have type aliases? - // See issue #23225. - n := len(p.typList) - if p.trackAllTypes { - p.record(nil) - } - - var embeddeds []types.Type - for n := p.int(); n > 0; n-- { - p.pos() - embeddeds = append(embeddeds, p.typ(parent, nil)) - } - - t := newInterface(p.methodList(parent, tname), embeddeds) - p.interfaceList = append(p.interfaceList, t) - if p.trackAllTypes { - p.typList[n] = t - } - return t - - case mapTag: - t := new(types.Map) - if p.trackAllTypes { - p.record(t) - } - - key := p.typ(parent, nil) - val := p.typ(parent, nil) - *t = *types.NewMap(key, val) - return t - - case chanTag: - t := new(types.Chan) - if p.trackAllTypes { - p.record(t) - } - - dir := chanDir(p.int()) - val := p.typ(parent, nil) - *t = *types.NewChan(dir, val) - return t - - default: - errorf("unexpected type tag %d", i) // panics - panic("unreachable") - } -} - func chanDir(d int) types.ChanDir { // tag values must match the constants in cmd/compile/internal/gc/go.go switch d { @@ -603,394 +88,6 @@ func chanDir(d int) types.ChanDir { } } -func (p *importer) fieldList(parent *types.Package) (fields []*types.Var, tags []string) { - if n := p.int(); n > 0 { - fields = make([]*types.Var, n) - tags = make([]string, n) - for i := range fields { - fields[i], tags[i] = p.field(parent) - } - } - return -} - -func (p *importer) field(parent *types.Package) (*types.Var, string) { - pos := p.pos() - pkg, name, alias := p.fieldName(parent) - typ := p.typ(parent, nil) - tag := p.string() - - anonymous := false - if name == "" { - // anonymous field - typ must be T or *T and T must be a type name - switch typ := deref(typ).(type) { - case *types.Basic: // basic types are named types - pkg = nil // // objects defined in Universe scope have no package - name = typ.Name() - case *types.Named: - name = typ.Obj().Name() - default: - errorf("named base type expected") - } - anonymous = true - } else if alias { - // anonymous field: we have an explicit name because it's an alias - anonymous = true - } - - return types.NewField(pos, pkg, name, typ, anonymous), tag -} - -func (p *importer) methodList(parent *types.Package, baseType *types.Named) (methods []*types.Func) { - if n := p.int(); n > 0 { - methods = make([]*types.Func, n) - for i := range methods { - methods[i] = p.method(parent, baseType) - } - } - return -} - -func (p *importer) method(parent *types.Package, baseType *types.Named) *types.Func { - pos := p.pos() - pkg, name, _ := p.fieldName(parent) - // If we don't have a baseType, use a nil receiver. - // A receiver using the actual interface type (which - // we don't know yet) will be filled in when we call - // types.Interface.Complete. - var recv *types.Var - if baseType != nil { - recv = types.NewVar(token.NoPos, parent, "", baseType) - } - params, isddd := p.paramList() - result, _ := p.paramList() - sig := types.NewSignature(recv, params, result, isddd) - return types.NewFunc(pos, pkg, name, sig) -} - -func (p *importer) fieldName(parent *types.Package) (pkg *types.Package, name string, alias bool) { - name = p.string() - pkg = parent - if pkg == nil { - // use the imported package instead - pkg = p.pkgList[0] - } - if p.version == 0 && name == "_" { - // version 0 didn't export a package for _ fields - return - } - switch name { - case "": - // 1) field name matches base type name and is exported: nothing to do - case "?": - // 2) field name matches base type name and is not exported: need package - name = "" - pkg = p.pkg() - case "@": - // 3) field name doesn't match type name (alias) - name = p.string() - alias = true - fallthrough - default: - if !exported(name) { - pkg = p.pkg() - } - } - return -} - -func (p *importer) paramList() (*types.Tuple, bool) { - n := p.int() - if n == 0 { - return nil, false - } - // negative length indicates unnamed parameters - named := true - if n < 0 { - n = -n - named = false - } - // n > 0 - params := make([]*types.Var, n) - isddd := false - for i := range params { - params[i], isddd = p.param(named) - } - return types.NewTuple(params...), isddd -} - -func (p *importer) param(named bool) (*types.Var, bool) { - t := p.typ(nil, nil) - td, isddd := t.(*dddSlice) - if isddd { - t = types.NewSlice(td.elem) - } - - var pkg *types.Package - var name string - if named { - name = p.string() - if name == "" { - errorf("expected named parameter") - } - if name != "_" { - pkg = p.pkg() - } - if i := strings.Index(name, "·"); i > 0 { - name = name[:i] // cut off gc-specific parameter numbering - } - } - - // read and discard compiler-specific info - p.string() - - return types.NewVar(token.NoPos, pkg, name, t), isddd -} - -func exported(name string) bool { - ch, _ := utf8.DecodeRuneInString(name) - return unicode.IsUpper(ch) -} - -func (p *importer) value() constant.Value { - switch tag := p.tagOrIndex(); tag { - case falseTag: - return constant.MakeBool(false) - case trueTag: - return constant.MakeBool(true) - case int64Tag: - return constant.MakeInt64(p.int64()) - case floatTag: - return p.float() - case complexTag: - re := p.float() - im := p.float() - return constant.BinaryOp(re, token.ADD, constant.MakeImag(im)) - case stringTag: - return constant.MakeString(p.string()) - case unknownTag: - return constant.MakeUnknown() - default: - errorf("unexpected value tag %d", tag) // panics - panic("unreachable") - } -} - -func (p *importer) float() constant.Value { - sign := p.int() - if sign == 0 { - return constant.MakeInt64(0) - } - - exp := p.int() - mant := []byte(p.string()) // big endian - - // remove leading 0's if any - for len(mant) > 0 && mant[0] == 0 { - mant = mant[1:] - } - - // convert to little endian - // TODO(gri) go/constant should have a more direct conversion function - // (e.g., once it supports a big.Float based implementation) - for i, j := 0, len(mant)-1; i < j; i, j = i+1, j-1 { - mant[i], mant[j] = mant[j], mant[i] - } - - // adjust exponent (constant.MakeFromBytes creates an integer value, - // but mant represents the mantissa bits such that 0.5 <= mant < 1.0) - exp -= len(mant) << 3 - if len(mant) > 0 { - for msd := mant[len(mant)-1]; msd&0x80 == 0; msd <<= 1 { - exp++ - } - } - - x := constant.MakeFromBytes(mant) - switch { - case exp < 0: - d := constant.Shift(constant.MakeInt64(1), token.SHL, uint(-exp)) - x = constant.BinaryOp(x, token.QUO, d) - case exp > 0: - x = constant.Shift(x, token.SHL, uint(exp)) - } - - if sign < 0 { - x = constant.UnaryOp(token.SUB, x, 0) - } - return x -} - -// ---------------------------------------------------------------------------- -// Low-level decoders - -func (p *importer) tagOrIndex() int { - if p.debugFormat { - p.marker('t') - } - - return int(p.rawInt64()) -} - -func (p *importer) int() int { - x := p.int64() - if int64(int(x)) != x { - errorf("exported integer too large") - } - return int(x) -} - -func (p *importer) int64() int64 { - if p.debugFormat { - p.marker('i') - } - - return p.rawInt64() -} - -func (p *importer) path() string { - if p.debugFormat { - p.marker('p') - } - // if the path was seen before, i is its index (>= 0) - // (the empty string is at index 0) - i := p.rawInt64() - if i >= 0 { - return p.pathList[i] - } - // otherwise, i is the negative path length (< 0) - a := make([]string, -i) - for n := range a { - a[n] = p.string() - } - s := strings.Join(a, "/") - p.pathList = append(p.pathList, s) - return s -} - -func (p *importer) string() string { - if p.debugFormat { - p.marker('s') - } - // if the string was seen before, i is its index (>= 0) - // (the empty string is at index 0) - i := p.rawInt64() - if i >= 0 { - return p.strList[i] - } - // otherwise, i is the negative string length (< 0) - if n := int(-i); n <= cap(p.buf) { - p.buf = p.buf[:n] - } else { - p.buf = make([]byte, n) - } - for i := range p.buf { - p.buf[i] = p.rawByte() - } - s := string(p.buf) - p.strList = append(p.strList, s) - return s -} - -func (p *importer) marker(want byte) { - if got := p.rawByte(); got != want { - errorf("incorrect marker: got %c; want %c (pos = %d)", got, want, p.read) - } - - pos := p.read - if n := int(p.rawInt64()); n != pos { - errorf("incorrect position: got %d; want %d", n, pos) - } -} - -// rawInt64 should only be used by low-level decoders. -func (p *importer) rawInt64() int64 { - i, err := binary.ReadVarint(p) - if err != nil { - errorf("read error: %v", err) - } - return i -} - -// rawStringln should only be used to read the initial version string. -func (p *importer) rawStringln(b byte) string { - p.buf = p.buf[:0] - for b != '\n' { - p.buf = append(p.buf, b) - b = p.rawByte() - } - return string(p.buf) -} - -// needed for binary.ReadVarint in rawInt64 -func (p *importer) ReadByte() (byte, error) { - return p.rawByte(), nil -} - -// byte is the bottleneck interface for reading p.data. -// It unescapes '|' 'S' to '$' and '|' '|' to '|'. -// rawByte should only be used by low-level decoders. -func (p *importer) rawByte() byte { - b := p.data[0] - r := 1 - if b == '|' { - b = p.data[1] - r = 2 - switch b { - case 'S': - b = '$' - case '|': - // nothing to do - default: - errorf("unexpected escape sequence in export data") - } - } - p.data = p.data[r:] - p.read += r - return b - -} - -// ---------------------------------------------------------------------------- -// Export format - -// Tags. Must be < 0. -const ( - // Objects - packageTag = -(iota + 1) - constTag - typeTag - varTag - funcTag - endTag - - // Types - namedTag - arrayTag - sliceTag - dddTag - structTag - pointerTag - signatureTag - interfaceTag - mapTag - chanTag - - // Values - falseTag - trueTag - int64Tag - floatTag - fractionTag // not used by gc - complexTag - stringTag - nilTag // only used by gc (appears in exported inlined function bodies) - unknownTag // not used by gc (only appears in packages with errors) - - // Type aliases - aliasTag -) - var predeclOnce sync.Once var predecl []types.Type // initialized lazily diff --git a/vendor/golang.org/x/tools/internal/gcimporter/gcimporter.go b/vendor/golang.org/x/tools/internal/gcimporter/gcimporter.go index a973dece..b1223713 100644 --- a/vendor/golang.org/x/tools/internal/gcimporter/gcimporter.go +++ b/vendor/golang.org/x/tools/internal/gcimporter/gcimporter.go @@ -230,20 +230,17 @@ func Import(packages map[string]*types.Package, path, srcDir string, lookup func // Or, define a new standard go/types/gcexportdata package. fset := token.NewFileSet() - // The indexed export format starts with an 'i'; the older - // binary export format starts with a 'c', 'd', or 'v' - // (from "version"). Select appropriate importer. + // Select appropriate importer. if len(data) > 0 { switch data[0] { - case 'i': - _, pkg, err := IImportData(fset, packages, data[1:], id) - return pkg, err + case 'v', 'c', 'd': // binary, till go1.10 + return nil, fmt.Errorf("binary (%c) import format is no longer supported", data[0]) - case 'v', 'c', 'd': - _, pkg, err := BImportData(fset, packages, data, id) + case 'i': // indexed, till go1.19 + _, pkg, err := IImportData(fset, packages, data[1:], id) return pkg, err - case 'u': + case 'u': // unified, from go1.20 _, pkg, err := UImportData(fset, packages, data[1:size], id) return pkg, err diff --git a/vendor/golang.org/x/tools/internal/gcimporter/iexport.go b/vendor/golang.org/x/tools/internal/gcimporter/iexport.go index a0dc0b5e..9930d8c3 100644 --- a/vendor/golang.org/x/tools/internal/gcimporter/iexport.go +++ b/vendor/golang.org/x/tools/internal/gcimporter/iexport.go @@ -969,6 +969,16 @@ func constantToFloat(x constant.Value) *big.Float { return &f } +func valueToRat(x constant.Value) *big.Rat { + // Convert little-endian to big-endian. + // I can't believe this is necessary. + bytes := constant.Bytes(x) + for i := 0; i < len(bytes)/2; i++ { + bytes[i], bytes[len(bytes)-1-i] = bytes[len(bytes)-1-i], bytes[i] + } + return new(big.Rat).SetInt(new(big.Int).SetBytes(bytes)) +} + // mpint exports a multi-precision integer. // // For unsigned types, small values are written out as a single @@ -1178,3 +1188,12 @@ func (q *objQueue) popHead() types.Object { q.head++ return obj } + +// internalError represents an error generated inside this package. +type internalError string + +func (e internalError) Error() string { return "gcimporter: " + string(e) } + +func internalErrorf(format string, args ...interface{}) error { + return internalError(fmt.Sprintf(format, args...)) +} diff --git a/vendor/golang.org/x/tools/internal/gcimporter/iimport.go b/vendor/golang.org/x/tools/internal/gcimporter/iimport.go index be6dace1..94a5eba3 100644 --- a/vendor/golang.org/x/tools/internal/gcimporter/iimport.go +++ b/vendor/golang.org/x/tools/internal/gcimporter/iimport.go @@ -131,7 +131,7 @@ func iimportCommon(fset *token.FileSet, getPackage GetPackageFunc, data []byte, } else if version > currentVersion { err = fmt.Errorf("cannot import %q (%v), export data is newer version - update tool", path, e) } else { - err = fmt.Errorf("cannot import %q (%v), possibly version skew - reinstall package", path, e) + err = fmt.Errorf("internal error while importing %q (%v); please report an issue", path, e) } } }() @@ -140,11 +140,8 @@ func iimportCommon(fset *token.FileSet, getPackage GetPackageFunc, data []byte, r := &intReader{bytes.NewReader(data), path} if bundle { - bundleVersion := r.uint64() - switch bundleVersion { - case bundleVersion: - default: - errorf("unknown bundle format version %d", bundleVersion) + if v := r.uint64(); v != bundleVersion { + errorf("unknown bundle format version %d", v) } } diff --git a/vendor/golang.org/x/tools/internal/gcimporter/ureader_yes.go b/vendor/golang.org/x/tools/internal/gcimporter/ureader_yes.go index 34fc783f..b977435f 100644 --- a/vendor/golang.org/x/tools/internal/gcimporter/ureader_yes.go +++ b/vendor/golang.org/x/tools/internal/gcimporter/ureader_yes.go @@ -10,6 +10,7 @@ package gcimporter import ( + "fmt" "go/token" "go/types" "sort" @@ -63,6 +64,14 @@ type typeInfo struct { } func UImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) { + if !debug { + defer func() { + if x := recover(); x != nil { + err = fmt.Errorf("internal error in importing %q (%v); please report an issue", path, x) + } + }() + } + s := string(data) s = s[:strings.LastIndex(s, "\n$$\n")] input := pkgbits.NewPkgDecoder(path, s) diff --git a/vendor/golang.org/x/tools/internal/gocommand/invoke.go b/vendor/golang.org/x/tools/internal/gocommand/invoke.go index 3c0afe72..8d9fc98d 100644 --- a/vendor/golang.org/x/tools/internal/gocommand/invoke.go +++ b/vendor/golang.org/x/tools/internal/gocommand/invoke.go @@ -24,6 +24,9 @@ import ( exec "golang.org/x/sys/execabs" "golang.org/x/tools/internal/event" + "golang.org/x/tools/internal/event/keys" + "golang.org/x/tools/internal/event/label" + "golang.org/x/tools/internal/event/tag" ) // An Runner will run go command invocations and serialize @@ -53,9 +56,19 @@ func (runner *Runner) initialize() { // 1.14: go: updating go.mod: existing contents have changed since last read var modConcurrencyError = regexp.MustCompile(`go:.*go.mod.*contents have changed`) +// verb is an event label for the go command verb. +var verb = keys.NewString("verb", "go command verb") + +func invLabels(inv Invocation) []label.Label { + return []label.Label{verb.Of(inv.Verb), tag.Directory.Of(inv.WorkingDir)} +} + // Run is a convenience wrapper around RunRaw. // It returns only stdout and a "friendly" error. func (runner *Runner) Run(ctx context.Context, inv Invocation) (*bytes.Buffer, error) { + ctx, done := event.Start(ctx, "gocommand.Runner.Run", invLabels(inv)...) + defer done() + stdout, _, friendly, _ := runner.RunRaw(ctx, inv) return stdout, friendly } @@ -63,6 +76,9 @@ func (runner *Runner) Run(ctx context.Context, inv Invocation) (*bytes.Buffer, e // RunPiped runs the invocation serially, always waiting for any concurrent // invocations to complete first. func (runner *Runner) RunPiped(ctx context.Context, inv Invocation, stdout, stderr io.Writer) error { + ctx, done := event.Start(ctx, "gocommand.Runner.RunPiped", invLabels(inv)...) + defer done() + _, err := runner.runPiped(ctx, inv, stdout, stderr) return err } @@ -70,6 +86,8 @@ func (runner *Runner) RunPiped(ctx context.Context, inv Invocation, stdout, stde // RunRaw runs the invocation, serializing requests only if they fight over // go.mod changes. func (runner *Runner) RunRaw(ctx context.Context, inv Invocation) (*bytes.Buffer, *bytes.Buffer, error, error) { + ctx, done := event.Start(ctx, "gocommand.Runner.RunRaw", invLabels(inv)...) + defer done() // Make sure the runner is always initialized. runner.initialize() diff --git a/vendor/golang.org/x/tools/internal/imports/fix.go b/vendor/golang.org/x/tools/internal/imports/fix.go index 6b493525..d4f1b4e8 100644 --- a/vendor/golang.org/x/tools/internal/imports/fix.go +++ b/vendor/golang.org/x/tools/internal/imports/fix.go @@ -26,6 +26,7 @@ import ( "unicode/utf8" "golang.org/x/tools/go/ast/astutil" + "golang.org/x/tools/internal/event" "golang.org/x/tools/internal/gocommand" "golang.org/x/tools/internal/gopathwalk" ) @@ -543,7 +544,7 @@ func (p *pass) addCandidate(imp *ImportInfo, pkg *packageInfo) { var fixImports = fixImportsDefault func fixImportsDefault(fset *token.FileSet, f *ast.File, filename string, env *ProcessEnv) error { - fixes, err := getFixes(fset, f, filename, env) + fixes, err := getFixes(context.Background(), fset, f, filename, env) if err != nil { return err } @@ -553,7 +554,7 @@ func fixImportsDefault(fset *token.FileSet, f *ast.File, filename string, env *P // getFixes gets the import fixes that need to be made to f in order to fix the imports. // It does not modify the ast. -func getFixes(fset *token.FileSet, f *ast.File, filename string, env *ProcessEnv) ([]*ImportFix, error) { +func getFixes(ctx context.Context, fset *token.FileSet, f *ast.File, filename string, env *ProcessEnv) ([]*ImportFix, error) { abs, err := filepath.Abs(filename) if err != nil { return nil, err @@ -607,7 +608,7 @@ func getFixes(fset *token.FileSet, f *ast.File, filename string, env *ProcessEnv // Go look for candidates in $GOPATH, etc. We don't necessarily load // the real exports of sibling imports, so keep assuming their contents. - if err := addExternalCandidates(p, p.missingRefs, filename); err != nil { + if err := addExternalCandidates(ctx, p, p.missingRefs, filename); err != nil { return nil, err } @@ -1055,7 +1056,10 @@ type scanCallback struct { exportsLoaded func(pkg *pkg, exports []string) } -func addExternalCandidates(pass *pass, refs references, filename string) error { +func addExternalCandidates(ctx context.Context, pass *pass, refs references, filename string) error { + ctx, done := event.Start(ctx, "imports.addExternalCandidates") + defer done() + var mu sync.Mutex found := make(map[string][]pkgDistance) callback := &scanCallback{ diff --git a/vendor/golang.org/x/tools/internal/imports/imports.go b/vendor/golang.org/x/tools/internal/imports/imports.go index 95a88383..58e637b9 100644 --- a/vendor/golang.org/x/tools/internal/imports/imports.go +++ b/vendor/golang.org/x/tools/internal/imports/imports.go @@ -11,6 +11,7 @@ package imports import ( "bufio" "bytes" + "context" "fmt" "go/ast" "go/format" @@ -23,6 +24,7 @@ import ( "strings" "golang.org/x/tools/go/ast/astutil" + "golang.org/x/tools/internal/event" ) // Options is golang.org/x/tools/imports.Options with extra internal-only options. @@ -66,14 +68,17 @@ func Process(filename string, src []byte, opt *Options) (formatted []byte, err e // // Note that filename's directory influences which imports can be chosen, // so it is important that filename be accurate. -func FixImports(filename string, src []byte, opt *Options) (fixes []*ImportFix, err error) { +func FixImports(ctx context.Context, filename string, src []byte, opt *Options) (fixes []*ImportFix, err error) { + ctx, done := event.Start(ctx, "imports.FixImports") + defer done() + fileSet := token.NewFileSet() file, _, err := parse(fileSet, filename, src, opt) if err != nil { return nil, err } - return getFixes(fileSet, file, filename, opt.Env) + return getFixes(ctx, fileSet, file, filename, opt.Env) } // ApplyFixes applies all of the fixes to the file and formats it. extraMode diff --git a/vendor/golang.org/x/tools/internal/imports/mod.go b/vendor/golang.org/x/tools/internal/imports/mod.go index 7d99d04c..1389d38b 100644 --- a/vendor/golang.org/x/tools/internal/imports/mod.go +++ b/vendor/golang.org/x/tools/internal/imports/mod.go @@ -19,6 +19,7 @@ import ( "strings" "golang.org/x/mod/module" + "golang.org/x/tools/internal/event" "golang.org/x/tools/internal/gocommand" "golang.org/x/tools/internal/gopathwalk" ) @@ -424,6 +425,9 @@ func (r *ModuleResolver) loadPackageNames(importPaths []string, srcDir string) ( } func (r *ModuleResolver) scan(ctx context.Context, callback *scanCallback) error { + ctx, done := event.Start(ctx, "imports.ModuleResolver.scan") + defer done() + if err := r.init(); err != nil { return err } diff --git a/vendor/golang.org/x/tools/internal/typesinternal/types.go b/vendor/golang.org/x/tools/internal/typesinternal/types.go index 3c53fbc6..ce7d4351 100644 --- a/vendor/golang.org/x/tools/internal/typesinternal/types.go +++ b/vendor/golang.org/x/tools/internal/typesinternal/types.go @@ -11,8 +11,6 @@ import ( "go/types" "reflect" "unsafe" - - "golang.org/x/tools/go/types/objectpath" ) func SetUsesCgo(conf *types.Config) bool { @@ -52,10 +50,3 @@ func ReadGo116ErrorData(err types.Error) (code ErrorCode, start, end token.Pos, } var SetGoVersion = func(conf *types.Config, version string) bool { return false } - -// NewObjectpathEncoder returns a function closure equivalent to -// objectpath.For but amortized for multiple (sequential) calls. -// It is a temporary workaround, pending the approval of proposal 58668. -// -//go:linkname NewObjectpathFunc golang.org/x/tools/go/types/objectpath.newEncoderFor -func NewObjectpathFunc() func(types.Object) (objectpath.Path, error) diff --git a/vendor/modules.txt b/vendor/modules.txt index 1bc0bbc4..f6e79f58 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -10,13 +10,16 @@ cloud.google.com/go/compute/internal # cloud.google.com/go/compute/metadata v0.2.3 ## explicit; go 1.19 cloud.google.com/go/compute/metadata +# github.com/4meepo/tagalign v1.2.2 +## explicit; go 1.19 +github.com/4meepo/tagalign # github.com/Abirdcfly/dupword v0.0.11 ## explicit; go 1.20 github.com/Abirdcfly/dupword -# github.com/Antonboom/errname v0.1.9 +# github.com/Antonboom/errname v0.1.10 ## explicit; go 1.20 github.com/Antonboom/errname/pkg/analyzer -# github.com/Antonboom/nilnil v0.1.3 +# github.com/Antonboom/nilnil v0.1.5 ## explicit; go 1.20 github.com/Antonboom/nilnil/pkg/analyzer # github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 @@ -42,7 +45,7 @@ github.com/Azure/go-autorest/logger # github.com/Azure/go-autorest/tracing v0.6.0 ## explicit; go 1.12 github.com/Azure/go-autorest/tracing -# github.com/BurntSushi/toml v1.2.1 +# github.com/BurntSushi/toml v1.3.0 ## explicit; go 1.16 github.com/BurntSushi/toml github.com/BurntSushi/toml/internal @@ -61,12 +64,16 @@ github.com/Masterminds/semver # github.com/NYTimes/gziphandler v1.1.1 ## explicit; go 1.11 github.com/NYTimes/gziphandler -# github.com/OpenPeeDeeP/depguard v1.1.1 -## explicit; go 1.13 -github.com/OpenPeeDeeP/depguard +# github.com/OpenPeeDeeP/depguard/v2 v2.0.1 +## explicit; go 1.20 +github.com/OpenPeeDeeP/depguard/v2 +github.com/OpenPeeDeeP/depguard/v2/internal/utils # github.com/PuerkitoBio/purell v1.2.0 ## explicit; go 1.19 github.com/PuerkitoBio/purell +# github.com/alexkohler/nakedret/v2 v2.0.1 +## explicit; go 1.18 +github.com/alexkohler/nakedret/v2 # github.com/alexkohler/prealloc v1.0.0 ## explicit; go 1.15 github.com/alexkohler/prealloc/pkg @@ -103,11 +110,15 @@ github.com/breml/bidichk/pkg/bidichk # github.com/breml/errchkjson v0.3.1 ## explicit; go 1.17 github.com/breml/errchkjson -# github.com/butuzov/ireturn v0.1.1 +# github.com/butuzov/ireturn v0.2.0 ## explicit; go 1.15 github.com/butuzov/ireturn/analyzer -github.com/butuzov/ireturn/config -github.com/butuzov/ireturn/types +github.com/butuzov/ireturn/analyzer/internal/config +github.com/butuzov/ireturn/analyzer/internal/types +# github.com/butuzov/mirror v1.1.0 +## explicit; go 1.19 +github.com/butuzov/mirror +github.com/butuzov/mirror/internal/checker # github.com/cespare/xxhash/v2 v2.2.0 ## explicit; go 1.11 github.com/cespare/xxhash/v2 @@ -208,7 +219,7 @@ github.com/fsnotify/fsnotify # github.com/fzipp/gocyclo v0.6.0 ## explicit; go 1.18 github.com/fzipp/gocyclo -# github.com/go-critic/go-critic v0.7.0 +# github.com/go-critic/go-critic v0.8.1 ## explicit; go 1.18 github.com/go-critic/go-critic/checkers github.com/go-critic/go-critic/checkers/internal/astwalk @@ -319,7 +330,7 @@ github.com/golangci/gofmt/gofmt github.com/golangci/gofmt/gofmt/internal/diff github.com/golangci/gofmt/gofmt/internal/execabs github.com/golangci/gofmt/goimports -# github.com/golangci/golangci-lint v1.52.2 +# github.com/golangci/golangci-lint v1.53.0 ## explicit; go 1.19 github.com/golangci/golangci-lint/cmd/golangci-lint github.com/golangci/golangci-lint/internal/cache @@ -345,7 +356,6 @@ github.com/golangci/golangci-lint/pkg/printers github.com/golangci/golangci-lint/pkg/report github.com/golangci/golangci-lint/pkg/result github.com/golangci/golangci-lint/pkg/result/processors -github.com/golangci/golangci-lint/pkg/sliceutil github.com/golangci/golangci-lint/pkg/timeutils # github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 ## explicit @@ -471,9 +481,6 @@ github.com/json-iterator/go # github.com/julz/importas v0.1.0 ## explicit; go 1.15 github.com/julz/importas -# github.com/junk1tm/musttag v0.5.0 -## explicit; go 1.19 -github.com/junk1tm/musttag # github.com/kisielk/errcheck v1.6.3 ## explicit; go 1.14 github.com/kisielk/errcheck/errcheck @@ -487,7 +494,7 @@ github.com/kkHAIKE/contextcheck # github.com/kulti/thelper v0.6.3 ## explicit; go 1.18 github.com/kulti/thelper/pkg/analyzer -# github.com/kunwardeep/paralleltest v1.0.6 +# github.com/kunwardeep/paralleltest v1.0.7 ## explicit; go 1.17 github.com/kunwardeep/paralleltest/pkg/paralleltest # github.com/kyoh86/exportloopref v0.1.11 @@ -496,7 +503,7 @@ github.com/kyoh86/exportloopref # github.com/ldez/gomoddirectives v0.2.3 ## explicit; go 1.16 github.com/ldez/gomoddirectives -# github.com/ldez/tagliatelle v0.4.0 +# github.com/ldez/tagliatelle v0.5.0 ## explicit; go 1.19 github.com/ldez/tagliatelle # github.com/leonklingele/grouper v1.1.1 @@ -545,7 +552,7 @@ github.com/matttproud/golang_protobuf_extensions/pbutil # github.com/mbilski/exhaustivestruct v1.2.0 ## explicit; go 1.15 github.com/mbilski/exhaustivestruct/pkg/analyzer -# github.com/mgechev/revive v1.3.1 +# github.com/mgechev/revive v1.3.2 ## explicit; go 1.19 github.com/mgechev/revive/config github.com/mgechev/revive/formatter @@ -593,18 +600,19 @@ github.com/nbutton23/zxcvbn-go/match github.com/nbutton23/zxcvbn-go/matching github.com/nbutton23/zxcvbn-go/scoring github.com/nbutton23/zxcvbn-go/utils/math -# github.com/nishanths/exhaustive v0.9.5 +# github.com/nishanths/exhaustive v0.10.0 ## explicit; go 1.14 github.com/nishanths/exhaustive # github.com/nishanths/predeclared v0.2.2 ## explicit; go 1.14 github.com/nishanths/predeclared/passes/predeclared -# github.com/nunnatsa/ginkgolinter v0.9.0 +# github.com/nunnatsa/ginkgolinter v0.12.0 ## explicit; go 1.19 github.com/nunnatsa/ginkgolinter github.com/nunnatsa/ginkgolinter/gomegahandler github.com/nunnatsa/ginkgolinter/reverseassertion github.com/nunnatsa/ginkgolinter/types +github.com/nunnatsa/ginkgolinter/version # github.com/nxadm/tail v1.4.8 ## explicit; go 1.13 github.com/nxadm/tail @@ -703,7 +711,7 @@ github.com/pkg/errors # github.com/pmezard/go-difflib v1.0.0 ## explicit github.com/pmezard/go-difflib/difflib -# github.com/polyfloyd/go-errorlint v1.4.0 +# github.com/polyfloyd/go-errorlint v1.4.2 ## explicit; go 1.20 github.com/polyfloyd/go-errorlint/errorlint # github.com/prometheus/client_golang v1.12.1 @@ -771,18 +779,20 @@ github.com/sashamelentyev/interfacebloat/pkg/analyzer ## explicit; go 1.19 github.com/sashamelentyev/usestdlibvars/pkg/analyzer github.com/sashamelentyev/usestdlibvars/pkg/analyzer/internal/mapping -# github.com/securego/gosec/v2 v2.15.0 +# github.com/securego/gosec/v2 v2.16.0 ## explicit; go 1.19 github.com/securego/gosec/v2 +github.com/securego/gosec/v2/analyzers github.com/securego/gosec/v2/cwe +github.com/securego/gosec/v2/issue github.com/securego/gosec/v2/rules # github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c ## explicit github.com/shazow/go-diff/difflib -# github.com/sirupsen/logrus v1.9.0 +# github.com/sirupsen/logrus v1.9.2 ## explicit; go 1.13 github.com/sirupsen/logrus -# github.com/sivchari/containedctx v1.0.2 +# github.com/sivchari/containedctx v1.0.3 ## explicit; go 1.17 github.com/sivchari/containedctx # github.com/sivchari/nosnakecase v1.7.0 @@ -836,8 +846,8 @@ github.com/stbenjam/no-sprintf-host-port/pkg/analyzer # github.com/stretchr/objx v0.5.0 ## explicit; go 1.12 github.com/stretchr/objx -# github.com/stretchr/testify v1.8.2 -## explicit; go 1.13 +# github.com/stretchr/testify v1.8.4 +## explicit; go 1.20 github.com/stretchr/testify/assert github.com/stretchr/testify/mock github.com/stretchr/testify/require @@ -853,7 +863,7 @@ github.com/tdakkota/asciicheck # github.com/tetafro/godot v1.4.11 ## explicit; go 1.16 github.com/tetafro/godot -# github.com/timakin/bodyclose v0.0.0-20221125081123-e39cf3fc478e +# github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966 ## explicit; go 1.12 github.com/timakin/bodyclose/passes/bodyclose # github.com/timonwong/loggercheck v0.9.4 @@ -882,6 +892,9 @@ github.com/ultraware/whitespace # github.com/uudashr/gocognit v1.0.6 ## explicit; go 1.16 github.com/uudashr/gocognit +# github.com/xen0n/gosmopolitan v1.2.1 +## explicit; go 1.19 +github.com/xen0n/gosmopolitan # github.com/xlab/treeprint v0.0.0-20181112141820-a009c3971eca ## explicit github.com/xlab/treeprint @@ -893,6 +906,9 @@ github.com/yagipy/maintidx/pkg/halstvol # github.com/yeya24/promlinter v0.2.0 ## explicit; go 1.16 github.com/yeya24/promlinter +# github.com/ykadowak/zerologlint v0.1.1 +## explicit; go 1.19 +github.com/ykadowak/zerologlint # gitlab.com/bosi/decorder v0.2.3 ## explicit; go 1.17 gitlab.com/bosi/decorder @@ -995,6 +1011,9 @@ go.starlark.net/resolve go.starlark.net/starlark go.starlark.net/starlarkstruct go.starlark.net/syntax +# go.tmz.dev/musttag v0.7.0 +## explicit; go 1.19 +go.tmz.dev/musttag # go.uber.org/atomic v1.7.0 ## explicit; go 1.13 go.uber.org/atomic @@ -1011,7 +1030,7 @@ go.uber.org/zap/internal/color go.uber.org/zap/internal/exit go.uber.org/zap/zapcore go.uber.org/zap/zapgrpc -# golang.org/x/crypto v0.6.0 +# golang.org/x/crypto v0.9.0 ## explicit; go 1.17 golang.org/x/crypto/cryptobyte golang.org/x/crypto/cryptobyte/asn1 @@ -1021,8 +1040,8 @@ golang.org/x/crypto/nacl/secretbox golang.org/x/crypto/pkcs12 golang.org/x/crypto/pkcs12/internal/rc2 golang.org/x/crypto/salsa20/salsa -# golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 -## explicit; go 1.18 +# golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea +## explicit; go 1.20 golang.org/x/exp/constraints golang.org/x/exp/slices # golang.org/x/exp/typeparams v0.0.0-20230224173230-c95f2b4c22f2 @@ -1103,7 +1122,7 @@ golang.org/x/text/width # golang.org/x/time v0.3.0 ## explicit golang.org/x/time/rate -# golang.org/x/tools v0.9.1 +# golang.org/x/tools v0.9.2 ## explicit; go 1.18 golang.org/x/tools/go/analysis golang.org/x/tools/go/analysis/passes/asmdecl @@ -1166,6 +1185,7 @@ golang.org/x/tools/internal/event golang.org/x/tools/internal/event/core golang.org/x/tools/internal/event/keys golang.org/x/tools/internal/event/label +golang.org/x/tools/internal/event/tag golang.org/x/tools/internal/fastwalk golang.org/x/tools/internal/gcimporter golang.org/x/tools/internal/gocommand @@ -2106,9 +2126,12 @@ k8s.io/utils/path k8s.io/utils/pointer k8s.io/utils/strings k8s.io/utils/trace -# mvdan.cc/gofumpt v0.4.0 -## explicit; go 1.18 +# mvdan.cc/gofumpt v0.5.0 +## explicit; go 1.19 mvdan.cc/gofumpt/format +mvdan.cc/gofumpt/internal/govendor/go/doc/comment +mvdan.cc/gofumpt/internal/govendor/go/format +mvdan.cc/gofumpt/internal/govendor/go/printer mvdan.cc/gofumpt/internal/version # mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed ## explicit diff --git a/vendor/mvdan.cc/gofumpt/format/format.go b/vendor/mvdan.cc/gofumpt/format/format.go index fa4f2fcb..78756178 100644 --- a/vendor/mvdan.cc/gofumpt/format/format.go +++ b/vendor/mvdan.cc/gofumpt/format/format.go @@ -10,7 +10,6 @@ import ( "bytes" "fmt" "go/ast" - "go/format" "go/parser" "go/token" "os" @@ -26,6 +25,7 @@ import ( "golang.org/x/mod/semver" "golang.org/x/tools/go/ast/astutil" + "mvdan.cc/gofumpt/internal/govendor/go/format" "mvdan.cc/gofumpt/internal/version" ) @@ -395,7 +395,7 @@ func (f *fumpter) applyPre(c *astutil.Cursor) { slc := []string{ "//gofumpt:diagnose", "version:", - version.String(), + version.String(""), "flags:", "-lang=" + f.LangVersion, "-modpath=" + f.ModulePath, @@ -467,13 +467,19 @@ func (f *fumpter) applyPre(c *astutil.Cursor) { specEnd := node.Specs[0].End() if len(f.commentsBetween(node.TokPos, specPos)) > 0 { - // If the single spec has any comment, it must - // go before the entire declaration now. + // If the single spec has a comment on the line above, + // the comment must go before the entire declaration now. node.TokPos = specPos } else { f.removeLines(f.Line(node.TokPos), f.Line(specPos)) } - f.removeLines(f.Line(specEnd), f.Line(node.Rparen)) + if len(f.commentsBetween(specEnd, node.Rparen)) > 0 { + // Leave one newline to not force a comment on the next line to + // become an inline comment. + f.removeLines(f.Line(specEnd)+1, f.Line(node.Rparen)) + } else { + f.removeLines(f.Line(specEnd), f.Line(node.Rparen)) + } // Remove the parentheses. go/printer will automatically // get rid of the newlines. @@ -546,12 +552,19 @@ func (f *fumpter) applyPre(c *astutil.Cursor) { if f.Line(sign.Pos()) != endLine { handleMultiLine := func(fl *ast.FieldList) { + // Refuse to insert a newline before the closing token + // if the list is empty or all in one line. if fl == nil || len(fl.List) == 0 { return } + fieldOpeningLine := f.Line(fl.Opening) + fieldClosingLine := f.Line(fl.Closing) + if fieldOpeningLine == fieldClosingLine { + return + } + lastFieldEnd := fl.List[len(fl.List)-1].End() lastFieldLine := f.Line(lastFieldEnd) - fieldClosingLine := f.Line(fl.Closing) isLastFieldOnFieldClosingLine := lastFieldLine == fieldClosingLine isLastFieldOnSigClosingLine := lastFieldLine == endLine diff --git a/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/doc.go b/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/doc.go new file mode 100644 index 00000000..45a476aa --- /dev/null +++ b/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/doc.go @@ -0,0 +1,36 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Package comment implements parsing and reformatting of Go doc comments, +(documentation comments), which are comments that immediately precede +a top-level declaration of a package, const, func, type, or var. + +Go doc comment syntax is a simplified subset of Markdown that supports +links, headings, paragraphs, lists (without nesting), and preformatted text blocks. +The details of the syntax are documented at https://go.dev/doc/comment. + +To parse the text associated with a doc comment (after removing comment markers), +use a [Parser]: + + var p comment.Parser + doc := p.Parse(text) + +The result is a [*Doc]. +To reformat it as a doc comment, HTML, Markdown, or plain text, +use a [Printer]: + + var pr comment.Printer + os.Stdout.Write(pr.Text(doc)) + +The [Parser] and [Printer] types are structs whose fields can be +modified to customize the operations. +For details, see the documentation for those types. + +Use cases that need additional control over reformatting can +implement their own logic by inspecting the parsed syntax itself. +See the documentation for [Doc], [Block], [Text] for an overview +and links to additional types. +*/ +package comment diff --git a/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/html.go b/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/html.go new file mode 100644 index 00000000..bc076f6a --- /dev/null +++ b/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/html.go @@ -0,0 +1,169 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package comment + +import ( + "bytes" + "fmt" + "strconv" +) + +// 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. +// See the [Printer] documentation for ways to customize the HTML output. +func (p *Printer) HTML(d *Doc) []byte { + hp := &htmlPrinter{Printer: p} + var out bytes.Buffer + for _, x := range d.Content { + hp.block(&out, x) + } + return out.Bytes() +} + +// block prints the block x to out. +func (p *htmlPrinter) block(out *bytes.Buffer, x Block) { + switch x := x.(type) { + default: + fmt.Fprintf(out, "?%T", x) + + case *Paragraph: + if !p.tight { + out.WriteString("

") + } + p.text(out, x.Text) + out.WriteString("\n") + + case *Heading: + out.WriteString("") + p.text(out, x.Text) + out.WriteString("\n") + + case *Code: + out.WriteString("

")
+		p.escape(out, x.Text)
+		out.WriteString("
\n") + + case *List: + kind := "ol>\n" + if x.Items[0].Number == "" { + kind = "ul>\n" + } + out.WriteString("<") + out.WriteString(kind) + next := "1" + for _, item := range x.Items { + out.WriteString("") + p.tight = !x.BlankBetween() + for _, blk := range item.Content { + p.block(out, blk) + } + p.tight = false + } + out.WriteString("= 0; i-- { + if b[i] < '9' { + b[i]++ + return string(b) + } + b[i] = '0' + } + return "1" + string(b) +} + +// text prints the text sequence x to out. +func (p *htmlPrinter) text(out *bytes.Buffer, x []Text) { + for _, t := range x { + switch t := t.(type) { + case Plain: + p.escape(out, string(t)) + case Italic: + out.WriteString("") + p.escape(out, string(t)) + out.WriteString("") + case *Link: + out.WriteString(``) + p.text(out, t.Text) + out.WriteString("") + case *DocLink: + url := p.docLinkURL(t) + if url != "" { + out.WriteString(``) + } + p.text(out, t.Text) + if url != "" { + out.WriteString("") + } + } + } +} + +// escape prints s to out as plain text, +// escaping < & " ' and > to avoid being misinterpreted +// in larger HTML constructs. +func (p *htmlPrinter) escape(out *bytes.Buffer, s string) { + start := 0 + for i := 0; i < len(s); i++ { + switch s[i] { + case '<': + out.WriteString(s[start:i]) + out.WriteString("<") + start = i + 1 + case '&': + out.WriteString(s[start:i]) + out.WriteString("&") + start = i + 1 + case '"': + out.WriteString(s[start:i]) + out.WriteString(""") + start = i + 1 + case '\'': + out.WriteString(s[start:i]) + out.WriteString("'") + start = i + 1 + case '>': + out.WriteString(s[start:i]) + out.WriteString(">") + start = i + 1 + } + } + out.WriteString(s[start:]) +} diff --git a/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/markdown.go b/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/markdown.go new file mode 100644 index 00000000..d8550f2e --- /dev/null +++ b/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/markdown.go @@ -0,0 +1,188 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package comment + +import ( + "bytes" + "fmt" + "strings" +) + +// An mdPrinter holds the state needed for printing a Doc as Markdown. +type mdPrinter struct { + *Printer + headingPrefix string + raw bytes.Buffer +} + +// Markdown returns a Markdown formatting of the Doc. +// See the [Printer] documentation for ways to customize the Markdown output. +func (p *Printer) Markdown(d *Doc) []byte { + mp := &mdPrinter{ + Printer: p, + headingPrefix: strings.Repeat("#", p.headingLevel()) + " ", + } + + var out bytes.Buffer + for i, x := range d.Content { + if i > 0 { + out.WriteByte('\n') + } + mp.block(&out, x) + } + return out.Bytes() +} + +// block prints the block x to out. +func (p *mdPrinter) block(out *bytes.Buffer, x Block) { + switch x := x.(type) { + default: + fmt.Fprintf(out, "?%T", x) + + case *Paragraph: + p.text(out, x.Text) + out.WriteString("\n") + + case *Heading: + out.WriteString(p.headingPrefix) + p.text(out, x.Text) + if id := p.headingID(x); id != "" { + out.WriteString(" {#") + out.WriteString(id) + out.WriteString("}") + } + out.WriteString("\n") + + case *Code: + md := x.Text + for md != "" { + var line string + line, md, _ = strings.Cut(md, "\n") + if line != "" { + out.WriteString("\t") + out.WriteString(line) + } + out.WriteString("\n") + } + + case *List: + loose := x.BlankBetween() + for i, item := range x.Items { + if i > 0 && loose { + out.WriteString("\n") + } + if n := item.Number; n != "" { + out.WriteString(" ") + out.WriteString(n) + out.WriteString(". ") + } else { + out.WriteString(" - ") // SP SP - SP + } + for i, blk := range item.Content { + const fourSpace = " " + if i > 0 { + out.WriteString("\n" + fourSpace) + } + p.text(out, blk.(*Paragraph).Text) + out.WriteString("\n") + } + } + } +} + +// text prints the text sequence x to out. +func (p *mdPrinter) text(out *bytes.Buffer, x []Text) { + p.raw.Reset() + p.rawText(&p.raw, x) + line := bytes.TrimSpace(p.raw.Bytes()) + if len(line) == 0 { + return + } + switch line[0] { + case '+', '-', '*', '#': + // Escape what would be the start of an unordered list or heading. + out.WriteByte('\\') + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + i := 1 + for i < len(line) && '0' <= line[i] && line[i] <= '9' { + i++ + } + if i < len(line) && (line[i] == '.' || line[i] == ')') { + // Escape what would be the start of an ordered list. + out.Write(line[:i]) + out.WriteByte('\\') + line = line[i:] + } + } + out.Write(line) +} + +// rawText prints the text sequence x to out, +// without worrying about escaping characters +// that have special meaning at the start of a Markdown line. +func (p *mdPrinter) rawText(out *bytes.Buffer, x []Text) { + for _, t := range x { + switch t := t.(type) { + case Plain: + p.escape(out, string(t)) + case Italic: + out.WriteString("*") + p.escape(out, string(t)) + out.WriteString("*") + case *Link: + out.WriteString("[") + p.rawText(out, t.Text) + out.WriteString("](") + out.WriteString(t.URL) + out.WriteString(")") + case *DocLink: + url := p.docLinkURL(t) + if url != "" { + out.WriteString("[") + } + p.rawText(out, t.Text) + if url != "" { + out.WriteString("](") + url = strings.ReplaceAll(url, "(", "%28") + url = strings.ReplaceAll(url, ")", "%29") + out.WriteString(url) + out.WriteString(")") + } + } + } +} + +// escape prints s to out as plain text, +// escaping special characters to avoid being misinterpreted +// as Markdown markup sequences. +func (p *mdPrinter) escape(out *bytes.Buffer, s string) { + start := 0 + for i := 0; i < len(s); i++ { + switch s[i] { + case '\n': + // Turn all \n into spaces, for a few reasons: + // - Avoid introducing paragraph breaks accidentally. + // - Avoid the need to reindent after the newline. + // - Avoid problems with Markdown renderers treating + // every mid-paragraph newline as a
. + out.WriteString(s[start:i]) + out.WriteByte(' ') + start = i + 1 + continue + case '`', '_', '*', '[', '<', '\\': + // Not all of these need to be escaped all the time, + // but is valid and easy to do so. + // We assume the Markdown is being passed to a + // Markdown renderer, not edited by a person, + // so it's fine to have escapes that are not strictly + // necessary in some cases. + out.WriteString(s[start:i]) + out.WriteByte('\\') + out.WriteByte(s[i]) + start = i + 1 + } + } + out.WriteString(s[start:]) +} diff --git a/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/parse.go b/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/parse.go new file mode 100644 index 00000000..372577b2 --- /dev/null +++ b/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/parse.go @@ -0,0 +1,1262 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package comment + +import ( + "sort" + "strings" + "unicode" + "unicode/utf8" +) + +// A Doc is a parsed Go doc comment. +type Doc struct { + // Content is the sequence of content blocks in the comment. + Content []Block + + // Links is the link definitions in the comment. + Links []*LinkDef +} + +// A LinkDef is a single link definition. +type LinkDef struct { + Text string // the link text + URL string // the link URL + Used bool // whether the comment uses the definition +} + +// A Block is block-level content in a doc comment, +// one of [*Code], [*Heading], [*List], or [*Paragraph]. +type Block interface { + block() +} + +// A Heading is a doc comment heading. +type Heading struct { + Text []Text // the heading text +} + +func (*Heading) block() {} + +// A List is a numbered or bullet list. +// Lists are always non-empty: len(Items) > 0. +// In a numbered list, every Items[i].Number is a non-empty string. +// In a bullet list, every Items[i].Number is an empty string. +type List struct { + // Items is the list items. + Items []*ListItem + + // ForceBlankBefore indicates that the list must be + // preceded by a blank line when reformatting the comment, + // overriding the usual conditions. See the BlankBefore method. + // + // The comment parser sets ForceBlankBefore for any list + // that is preceded by a blank line, to make sure + // the blank line is preserved when printing. + ForceBlankBefore bool + + // ForceBlankBetween indicates that list items must be + // separated by blank lines when reformatting the comment, + // overriding the usual conditions. See the BlankBetween method. + // + // The comment parser sets ForceBlankBetween for any list + // that has a blank line between any two of its items, to make sure + // the blank lines are preserved when printing. + ForceBlankBetween bool +} + +func (*List) block() {} + +// BlankBefore reports whether a reformatting of the comment +// should include a blank line before the list. +// The default rule is the same as for [BlankBetween]: +// if the list item content contains any blank lines +// (meaning at least one item has multiple paragraphs) +// then the list itself must be preceded by a blank line. +// A preceding blank line can be forced by setting [List].ForceBlankBefore. +func (l *List) BlankBefore() bool { + return l.ForceBlankBefore || l.BlankBetween() +} + +// BlankBetween reports whether a reformatting of the comment +// should include a blank line between each pair of list items. +// The default rule is that if the list item content contains any blank lines +// (meaning at least one item has multiple paragraphs) +// then list items must themselves be separated by blank lines. +// Blank line separators can be forced by setting [List].ForceBlankBetween. +func (l *List) BlankBetween() bool { + if l.ForceBlankBetween { + return true + } + for _, item := range l.Items { + if len(item.Content) != 1 { + // Unreachable for parsed comments today, + // since the only way to get multiple item.Content + // is multiple paragraphs, which must have been + // separated by a blank line. + return true + } + } + return false +} + +// A ListItem is a single item in a numbered or bullet list. +type ListItem struct { + // Number is a decimal string in a numbered list + // or an empty string in a bullet list. + Number string // "1", "2", ...; "" for bullet list + + // Content is the list content. + // Currently, restrictions in the parser and printer + // require every element of Content to be a *Paragraph. + Content []Block // Content of this item. +} + +// A Paragraph is a paragraph of text. +type Paragraph struct { + Text []Text +} + +func (*Paragraph) block() {} + +// A Code is a preformatted code block. +type Code struct { + // Text is the preformatted text, ending with a newline character. + // It may be multiple lines, each of which ends with a newline character. + // It is never empty, nor does it start or end with a blank line. + Text string +} + +func (*Code) block() {} + +// A Text is text-level content in a doc comment, +// one of [Plain], [Italic], [*Link], or [*DocLink]. +type Text interface { + text() +} + +// A Plain is a string rendered as plain text (not italicized). +type Plain string + +func (Plain) text() {} + +// An Italic is a string rendered as italicized text. +type Italic string + +func (Italic) text() {} + +// A Link is a link to a specific URL. +type Link struct { + Auto bool // is this an automatic (implicit) link of a literal URL? + Text []Text // text of link + URL string // target URL of link +} + +func (*Link) text() {} + +// A DocLink is a link to documentation for a Go package or symbol. +type DocLink struct { + Text []Text // text of link + + // ImportPath, Recv, and Name identify the Go package or symbol + // that is the link target. The potential combinations of + // non-empty fields are: + // - ImportPath: a link to another package + // - ImportPath, Name: a link to a const, func, type, or var in another package + // - ImportPath, Recv, Name: a link to a method in another package + // - Name: a link to a const, func, type, or var in this package + // - Recv, Name: a link to a method in this package + ImportPath string // import path + Recv string // receiver type, without any pointer star, for methods + Name string // const, func, type, var, or method name +} + +func (*DocLink) text() {} + +// A Parser is a doc comment parser. +// The fields in the struct can be filled in before calling Parse +// in order to customize the details of the parsing process. +type Parser struct { + // Words is a map of Go identifier words that + // should be italicized and potentially linked. + // If Words[w] is the empty string, then the word w + // is only italicized. Otherwise it is linked, using + // Words[w] as the link target. + // Words corresponds to the [go/doc.ToHTML] words parameter. + Words map[string]string + + // LookupPackage resolves a package name to an import path. + // + // If LookupPackage(name) returns ok == true, then [name] + // (or [name.Sym] or [name.Sym.Method]) + // is considered a documentation link to importPath's package docs. + // It is valid to return "", true, in which case name is considered + // to refer to the current package. + // + // If LookupPackage(name) returns ok == false, + // then [name] (or [name.Sym] or [name.Sym.Method]) + // will not be considered a documentation link, + // except in the case where name is the full (but single-element) import path + // of a package in the standard library, such as in [math] or [io.Reader]. + // LookupPackage is still called for such names, + // in order to permit references to imports of other packages + // with the same package names. + // + // Setting LookupPackage to nil is equivalent to setting it to + // a function that always returns "", false. + LookupPackage func(name string) (importPath string, ok bool) + + // LookupSym reports whether a symbol name or method name + // exists in the current package. + // + // If LookupSym("", "Name") returns true, then [Name] + // is considered a documentation link for a const, func, type, or var. + // + // Similarly, if LookupSym("Recv", "Name") returns true, + // then [Recv.Name] is considered a documentation link for + // type Recv's method Name. + // + // Setting LookupSym to nil is equivalent to setting it to a function + // that always returns false. + LookupSym func(recv, name string) (ok bool) +} + +// parseDoc is parsing state for a single doc comment. +type parseDoc struct { + *Parser + *Doc + links map[string]*LinkDef + lines []string + lookupSym func(recv, name string) bool +} + +// lookupPkg is called to look up the pkg in [pkg], [pkg.Name], and [pkg.Name.Recv]. +// If pkg has a slash, it is assumed to be the full import path and is returned with ok = true. +// +// Otherwise, pkg is probably a simple package name like "rand" (not "crypto/rand" or "math/rand"). +// d.LookupPackage provides a way for the caller to allow resolving such names with reference +// to the imports in the surrounding package. +// +// There is one collision between these two cases: single-element standard library names +// like "math" are full import paths but don't contain slashes. We let d.LookupPackage have +// the first chance to resolve it, in case there's a different package imported as math, +// and otherwise we refer to a built-in list of single-element standard library package names. +func (d *parseDoc) lookupPkg(pkg string) (importPath string, ok bool) { + if strings.Contains(pkg, "/") { // assume a full import path + if validImportPath(pkg) { + return pkg, true + } + return "", false + } + if d.LookupPackage != nil { + // Give LookupPackage a chance. + if path, ok := d.LookupPackage(pkg); ok { + return path, true + } + } + return DefaultLookupPackage(pkg) +} + +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 +} + +// DefaultLookupPackage is the default package lookup +// 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. +// +// Note that the go/doc package provides a more sophisticated +// lookup based on the imports used in the current package. +func DefaultLookupPackage(name string) (importPath string, ok bool) { + if isStdPkg(name) { + return name, true + } + return "", false +} + +// 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")) + d := &parseDoc{ + Parser: p, + Doc: new(Doc), + links: make(map[string]*LinkDef), + lines: lines, + lookupSym: func(recv, name string) bool { return false }, + } + if p.LookupSym != nil { + d.lookupSym = p.LookupSym + } + + // First pass: break into block structure and collect known links. + // The text is all recorded as Plain for now. + var prev span + for _, s := range parseSpans(lines) { + var b Block + switch s.kind { + default: + panic("mvdan.cc/gofumpt/internal/govendor/go/doc/comment: internal error: unknown span kind") + case spanList: + b = d.list(lines[s.start:s.end], prev.end < s.start) + case spanCode: + b = d.code(lines[s.start:s.end]) + case spanOldHeading: + b = d.oldHeading(lines[s.start]) + case spanHeading: + b = d.heading(lines[s.start]) + case spanPara: + b = d.paragraph(lines[s.start:s.end]) + } + if b != nil { + d.Content = append(d.Content, b) + } + prev = s + } + + // Second pass: interpret all the Plain text now that we know the links. + for _, b := range d.Content { + switch b := b.(type) { + case *Paragraph: + b.Text = d.parseLinkedText(string(b.Text[0].(Plain))) + case *List: + for _, i := range b.Items { + for _, c := range i.Content { + p := c.(*Paragraph) + p.Text = d.parseLinkedText(string(p.Text[0].(Plain))) + } + } + } + } + + return d.Doc +} + +// A span represents a single span of comment lines (lines[start:end]) +// of an identified kind (code, heading, paragraph, and so on). +type span struct { + start int + end int + kind spanKind +} + +// A spanKind describes the kind of span. +type spanKind int + +const ( + _ spanKind = iota + spanCode + spanHeading + spanList + spanOldHeading + spanPara +) + +func parseSpans(lines []string) []span { + var spans []span + + // The loop may process a line twice: once as unindented + // and again forced indented. So the maximum expected + // number of iterations is 2*len(lines). The repeating logic + // can be subtle, though, and to protect against introduction + // of infinite loops in future changes, we watch to see that + // we are not looping too much. A panic is better than a + // quiet infinite loop. + watchdog := 2 * len(lines) + + i := 0 + forceIndent := 0 +Spans: + for { + // Skip blank lines. + for i < len(lines) && lines[i] == "" { + i++ + } + if i >= len(lines) { + break + } + if watchdog--; watchdog < 0 { + panic("mvdan.cc/gofumpt/internal/govendor/go/doc/comment: internal error: not making progress") + } + + var kind spanKind + start := i + end := i + if i < forceIndent || indented(lines[i]) { + // Indented (or force indented). + // Ends before next unindented. (Blank lines are OK.) + // If this is an unindented list that we are heuristically treating as indented, + // then accept unindented list item lines up to the first blank lines. + // The heuristic is disabled at blank lines to contain its effect + // to non-gofmt'ed sections of the comment. + unindentedListOK := isList(lines[i]) && i < forceIndent + i++ + for i < len(lines) && (lines[i] == "" || i < forceIndent || indented(lines[i]) || (unindentedListOK && isList(lines[i]))) { + if lines[i] == "" { + unindentedListOK = false + } + i++ + } + + // Drop trailing blank lines. + end = i + for end > start && lines[end-1] == "" { + end-- + } + + // If indented lines are followed (without a blank line) + // by an unindented line ending in a brace, + // take that one line too. This fixes the common mistake + // of pasting in something like + // + // func main() { + // fmt.Println("hello, world") + // } + // + // and forgetting to indent it. + // The heuristic will never trigger on a gofmt'ed comment, + // because any gofmt'ed code block or list would be + // followed by a blank line or end of comment. + if end < len(lines) && strings.HasPrefix(lines[end], "}") { + end++ + } + + if isList(lines[start]) { + kind = spanList + } else { + kind = spanCode + } + } else { + // Unindented. Ends at next blank or indented line. + i++ + for i < len(lines) && lines[i] != "" && !indented(lines[i]) { + i++ + } + end = i + + // If unindented lines are followed (without a blank line) + // by an indented line that would start a code block, + // check whether the final unindented lines + // should be left for the indented section. + // This can happen for the common mistakes of + // unindented code or unindented lists. + // The heuristic will never trigger on a gofmt'ed comment, + // because any gofmt'ed code block would have a blank line + // preceding it after the unindented lines. + if i < len(lines) && lines[i] != "" && !isList(lines[i]) { + switch { + case isList(lines[i-1]): + // If the final unindented line looks like a list item, + // this may be the first indented line wrap of + // a mistakenly unindented list. + // Leave all the unindented list items. + forceIndent = end + end-- + for end > start && isList(lines[end-1]) { + end-- + } + + case strings.HasSuffix(lines[i-1], "{") || strings.HasSuffix(lines[i-1], `\`): + // If the final unindented line ended in { or \ + // it is probably the start of a misindented code block. + // Give the user a single line fix. + // Often that's enough; if not, the user can fix the others themselves. + forceIndent = end + end-- + } + + if start == end && forceIndent > start { + i = start + continue Spans + } + } + + // Span is either paragraph or heading. + if end-start == 1 && isHeading(lines[start]) { + kind = spanHeading + } else if end-start == 1 && isOldHeading(lines[start], lines, start) { + kind = spanOldHeading + } else { + kind = spanPara + } + } + + spans = append(spans, span{start, end, kind}) + i = end + } + + return spans +} + +// indented reports whether line is indented +// (starts with a leading space or tab). +func indented(line string) bool { + return line != "" && (line[0] == ' ' || line[0] == '\t') +} + +// unindent removes any common space/tab prefix +// from each line in lines, returning a copy of lines in which +// those prefixes have been trimmed from each line. +// It also replaces any lines containing only spaces with blank lines (empty strings). +func unindent(lines []string) []string { + // Trim leading and trailing blank lines. + for len(lines) > 0 && isBlank(lines[0]) { + lines = lines[1:] + } + for len(lines) > 0 && isBlank(lines[len(lines)-1]) { + lines = lines[:len(lines)-1] + } + if len(lines) == 0 { + return nil + } + + // Compute and remove common indentation. + prefix := leadingSpace(lines[0]) + for _, line := range lines[1:] { + if !isBlank(line) { + prefix = commonPrefix(prefix, leadingSpace(line)) + } + } + + out := make([]string, len(lines)) + for i, line := range lines { + line = strings.TrimPrefix(line, prefix) + if strings.TrimSpace(line) == "" { + line = "" + } + out[i] = line + } + for len(out) > 0 && out[0] == "" { + out = out[1:] + } + for len(out) > 0 && out[len(out)-1] == "" { + out = out[:len(out)-1] + } + return out +} + +// isBlank reports whether s is a blank line. +func isBlank(s string) bool { + return len(s) == 0 || (len(s) == 1 && s[0] == '\n') +} + +// commonPrefix returns the longest common prefix of a and b. +func commonPrefix(a, b string) string { + i := 0 + for i < len(a) && i < len(b) && a[i] == b[i] { + i++ + } + return a[0:i] +} + +// leadingSpace returns the longest prefix of s consisting of spaces and tabs. +func leadingSpace(s string) string { + i := 0 + for i < len(s) && (s[i] == ' ' || s[i] == '\t') { + i++ + } + return s[:i] +} + +// isOldHeading reports whether line is an old-style section heading. +// line is all[off]. +func isOldHeading(line string, all []string, off int) bool { + if off <= 0 || all[off-1] != "" || off+2 >= len(all) || all[off+1] != "" || leadingSpace(all[off+2]) != "" { + return false + } + + line = strings.TrimSpace(line) + + // a heading must start with an uppercase letter + r, _ := utf8.DecodeRuneInString(line) + if !unicode.IsLetter(r) || !unicode.IsUpper(r) { + return false + } + + // it must end in a letter or digit: + r, _ = utf8.DecodeLastRuneInString(line) + if !unicode.IsLetter(r) && !unicode.IsDigit(r) { + return false + } + + // exclude lines with illegal characters. we allow "()," + if strings.ContainsAny(line, ";:!?+*/=[]{}_^°&§~%#@<\">\\") { + return false + } + + // allow "'" for possessive "'s" only + for b := line; ; { + var ok bool + if _, b, ok = strings.Cut(b, "'"); !ok { + break + } + if b != "s" && !strings.HasPrefix(b, "s ") { + return false // ' not followed by s and then end-of-word + } + } + + // allow "." when followed by non-space + for b := line; ; { + var ok bool + if _, b, ok = strings.Cut(b, "."); !ok { + break + } + if b == "" || strings.HasPrefix(b, " ") { + return false // not followed by non-space + } + } + + return true +} + +// oldHeading returns the *Heading for the given old-style section heading line. +func (d *parseDoc) oldHeading(line string) Block { + return &Heading{Text: []Text{Plain(strings.TrimSpace(line))}} +} + +// isHeading reports whether line is a new-style section heading. +func isHeading(line string) bool { + return len(line) >= 2 && + line[0] == '#' && + (line[1] == ' ' || line[1] == '\t') && + strings.TrimSpace(line) != "#" +} + +// heading returns the *Heading for the given new-style section heading line. +func (d *parseDoc) heading(line string) Block { + return &Heading{Text: []Text{Plain(strings.TrimSpace(line[1:]))}} +} + +// code returns a code block built from the lines. +func (d *parseDoc) code(lines []string) *Code { + body := unindent(lines) + body = append(body, "") // to get final \n from Join + return &Code{Text: strings.Join(body, "\n")} +} + +// paragraph returns a paragraph block built from the lines. +// If the lines are link definitions, paragraph adds them to d and returns nil. +func (d *parseDoc) paragraph(lines []string) Block { + // Is this a block of known links? Handle. + var defs []*LinkDef + for _, line := range lines { + def, ok := parseLink(line) + if !ok { + goto NoDefs + } + defs = append(defs, def) + } + for _, def := range defs { + d.Links = append(d.Links, def) + if d.links[def.Text] == nil { + d.links[def.Text] = def + } + } + return nil +NoDefs: + + return &Paragraph{Text: []Text{Plain(strings.Join(lines, "\n"))}} +} + +// parseLink parses a single link definition line: +// +// [text]: url +// +// It returns the link definition and whether the line was well formed. +func parseLink(line string) (*LinkDef, bool) { + if line == "" || line[0] != '[' { + return nil, false + } + i := strings.Index(line, "]:") + if i < 0 || i+3 >= len(line) || (line[i+2] != ' ' && line[i+2] != '\t') { + return nil, false + } + + text := line[1:i] + url := strings.TrimSpace(line[i+3:]) + j := strings.Index(url, "://") + if j < 0 || !isScheme(url[:j]) { + return nil, false + } + + // Line has right form and has valid scheme://. + // That's good enough for us - we are not as picky + // about the characters beyond the :// as we are + // when extracting inline URLs from text. + return &LinkDef{Text: text, URL: url}, true +} + +// list returns a list built from the indented lines, +// using forceBlankBefore as the value of the List's ForceBlankBefore field. +func (d *parseDoc) list(lines []string, forceBlankBefore bool) *List { + num, _, _ := listMarker(lines[0]) + var ( + list *List = &List{ForceBlankBefore: forceBlankBefore} + item *ListItem + text []string + ) + flush := func() { + if item != nil { + if para := d.paragraph(text); para != nil { + item.Content = append(item.Content, para) + } + } + text = nil + } + + for _, line := range lines { + if n, after, ok := listMarker(line); ok && (n != "") == (num != "") { + // start new list item + flush() + + item = &ListItem{Number: n} + list.Items = append(list.Items, item) + line = after + } + line = strings.TrimSpace(line) + if line == "" { + list.ForceBlankBetween = true + flush() + continue + } + text = append(text, strings.TrimSpace(line)) + } + flush() + return list +} + +// listMarker parses the line as beginning with a list marker. +// If it can do that, it returns the numeric marker ("" for a bullet list), +// the rest of the line, and ok == true. +// Otherwise, it returns "", "", false. +func listMarker(line string) (num, rest string, ok bool) { + line = strings.TrimSpace(line) + if line == "" { + return "", "", false + } + + // Can we find a marker? + if r, n := utf8.DecodeRuneInString(line); r == '•' || r == '*' || r == '+' || r == '-' { + num, rest = "", line[n:] + } else if '0' <= line[0] && line[0] <= '9' { + n := 1 + for n < len(line) && '0' <= line[n] && line[n] <= '9' { + n++ + } + if n >= len(line) || (line[n] != '.' && line[n] != ')') { + return "", "", false + } + num, rest = line[:n], line[n+1:] + } else { + return "", "", false + } + + if !indented(rest) || strings.TrimSpace(rest) == "" { + return "", "", false + } + + return num, rest, true +} + +// isList reports whether the line is the first line of a list, +// meaning starts with a list marker after any indentation. +// (The caller is responsible for checking the line is indented, as appropriate.) +func isList(line string) bool { + _, _, ok := listMarker(line) + return ok +} + +// parseLinkedText parses text that is allowed to contain explicit links, +// such as [math.Sin] or [Go home page], into a slice of Text items. +// +// A “pkg” is only assumed to be a full import path if it starts with +// a domain name (a path element with a dot) or is one of the packages +// from the standard library (“[os]”, “[encoding/json]”, and so on). +// To avoid problems with maps, generics, and array types, doc links +// must be both preceded and followed by punctuation, spaces, tabs, +// or the start or end of a line. An example problem would be treating +// map[ast.Expr]TypeAndValue as containing a link. +func (d *parseDoc) parseLinkedText(text string) []Text { + var out []Text + wrote := 0 + flush := func(i int) { + if wrote < i { + out = d.parseText(out, text[wrote:i], true) + wrote = i + } + } + + start := -1 + var buf []byte + for i := 0; i < len(text); i++ { + c := text[i] + if c == '\n' || c == '\t' { + c = ' ' + } + switch c { + case '[': + start = i + case ']': + if start >= 0 { + if def, ok := d.links[string(buf)]; ok { + def.Used = true + flush(start) + out = append(out, &Link{ + Text: d.parseText(nil, text[start+1:i], false), + URL: def.URL, + }) + wrote = i + 1 + } else if link, ok := d.docLink(text[start+1:i], text[:start], text[i+1:]); ok { + flush(start) + link.Text = d.parseText(nil, text[start+1:i], false) + out = append(out, link) + wrote = i + 1 + } + } + start = -1 + buf = buf[:0] + } + if start >= 0 && i != start { + buf = append(buf, c) + } + } + + flush(len(text)) + return out +} + +// docLink parses text, which was found inside [ ] brackets, +// as a doc link if possible, returning the DocLink and ok == true +// or else nil, false. +// The before and after strings are the text before the [ and after the ] +// on the same line. Doc links must be preceded and followed by +// punctuation, spaces, tabs, or the start or end of a line. +func (d *parseDoc) docLink(text, before, after string) (link *DocLink, ok bool) { + if before != "" { + r, _ := utf8.DecodeLastRuneInString(before) + if !unicode.IsPunct(r) && r != ' ' && r != '\t' && r != '\n' { + return nil, false + } + } + if after != "" { + r, _ := utf8.DecodeRuneInString(after) + if !unicode.IsPunct(r) && r != ' ' && r != '\t' && r != '\n' { + return nil, false + } + } + text = strings.TrimPrefix(text, "*") + pkg, name, ok := splitDocName(text) + var recv string + if ok { + pkg, recv, _ = splitDocName(pkg) + } + if pkg != "" { + if pkg, ok = d.lookupPkg(pkg); !ok { + return nil, false + } + } else { + if ok = d.lookupSym(recv, name); !ok { + return nil, false + } + } + link = &DocLink{ + ImportPath: pkg, + Recv: recv, + Name: name, + } + return link, true +} + +// If text is of the form before.Name, where Name is a capitalized Go identifier, +// then splitDocName returns before, name, true. +// Otherwise it returns text, "", false. +func splitDocName(text string) (before, name string, foundDot bool) { + i := strings.LastIndex(text, ".") + name = text[i+1:] + if !isName(name) { + return text, "", false + } + if i >= 0 { + before = text[:i] + } + return before, name, true +} + +// parseText parses s as text and returns the result of appending +// those parsed Text elements to out. +// parseText does not handle explicit links like [math.Sin] or [Go home page]: +// those are handled by parseLinkedText. +// If autoLink is true, then parseText recognizes URLs and words from d.Words +// and converts those to links as appropriate. +func (d *parseDoc) parseText(out []Text, s string, autoLink bool) []Text { + var w strings.Builder + wrote := 0 + writeUntil := func(i int) { + w.WriteString(s[wrote:i]) + wrote = i + } + flush := func(i int) { + writeUntil(i) + if w.Len() > 0 { + out = append(out, Plain(w.String())) + w.Reset() + } + } + for i := 0; i < len(s); { + t := s[i:] + if autoLink { + if url, ok := autoURL(t); ok { + flush(i) + // Note: The old comment parser would look up the URL in words + // and replace the target with words[URL] if it was non-empty. + // That would allow creating links that display as one URL but + // when clicked go to a different URL. Not sure what the point + // of that is, so we're not doing that lookup here. + out = append(out, &Link{Auto: true, Text: []Text{Plain(url)}, URL: url}) + i += len(url) + wrote = i + continue + } + if id, ok := ident(t); ok { + url, italics := d.Words[id] + if !italics { + i += len(id) + continue + } + flush(i) + if url == "" { + out = append(out, Italic(id)) + } else { + out = append(out, &Link{Auto: true, Text: []Text{Italic(id)}, URL: url}) + } + i += len(id) + wrote = i + continue + } + } + switch { + case strings.HasPrefix(t, "``"): + if len(t) >= 3 && t[2] == '`' { + // Do not convert `` inside ```, in case people are mistakenly writing Markdown. + i += 3 + for i < len(t) && t[i] == '`' { + i++ + } + break + } + writeUntil(i) + w.WriteRune('“') + i += 2 + wrote = i + case strings.HasPrefix(t, "''"): + writeUntil(i) + w.WriteRune('”') + i += 2 + wrote = i + default: + i++ + } + } + flush(len(s)) + return out +} + +// autoURL checks whether s begins with a URL that should be hyperlinked. +// If so, it returns the URL, which is a prefix of s, and ok == true. +// Otherwise it returns "", false. +// The caller should skip over the first len(url) bytes of s +// before further processing. +func autoURL(s string) (url string, ok bool) { + // Find the ://. Fast path to pick off non-URL, + // since we call this at every position in the string. + // The shortest possible URL is ftp://x, 7 bytes. + var i int + switch { + case len(s) < 7: + return "", false + case s[3] == ':': + i = 3 + case s[4] == ':': + i = 4 + case s[5] == ':': + i = 5 + case s[6] == ':': + i = 6 + default: + return "", false + } + if i+3 > len(s) || s[i:i+3] != "://" { + return "", false + } + + // Check valid scheme. + if !isScheme(s[:i]) { + return "", false + } + + // Scan host part. Must have at least one byte, + // and must start and end in non-punctuation. + i += 3 + if i >= len(s) || !isHost(s[i]) || isPunct(s[i]) { + return "", false + } + i++ + end := i + for i < len(s) && isHost(s[i]) { + if !isPunct(s[i]) { + end = i + 1 + } + i++ + } + i = end + + // At this point we are definitely returning a URL (scheme://host). + // We just have to find the longest path we can add to it. + // Heuristics abound. + // We allow parens, braces, and brackets, + // but only if they match (#5043, #22285). + // We allow .,:;?! in the path but not at the end, + // to avoid end-of-sentence punctuation (#18139, #16565). + stk := []byte{} + end = i +Path: + for ; i < len(s); i++ { + if isPunct(s[i]) { + continue + } + if !isPath(s[i]) { + break + } + switch s[i] { + case '(': + stk = append(stk, ')') + case '{': + stk = append(stk, '}') + case '[': + stk = append(stk, ']') + case ')', '}', ']': + if len(stk) == 0 || stk[len(stk)-1] != s[i] { + break Path + } + stk = stk[:len(stk)-1] + } + if len(stk) == 0 { + end = i + 1 + } + } + + return s[:end], true +} + +// isScheme reports whether s is a recognized URL scheme. +// Note that if strings of new length (beyond 3-7) +// are added here, the fast path at the top of autoURL will need updating. +func isScheme(s string) bool { + switch s { + case "file", + "ftp", + "gopher", + "http", + "https", + "mailto", + "nntp": + return true + } + return false +} + +// isHost reports whether c is a byte that can appear in a URL host, +// like www.example.com or user@[::1]:8080 +func isHost(c byte) bool { + // mask is a 128-bit bitmap with 1s for allowed bytes, + // so that the byte c can be tested with a shift and an and. + // If c > 128, then 1<>64)) != 0 +} + +// isPunct reports whether c is a punctuation byte that can appear +// inside a path but not at the end. +func isPunct(c byte) bool { + // mask is a 128-bit bitmap with 1s for allowed bytes, + // so that the byte c can be tested with a shift and an and. + // If c > 128, then 1<>64)) != 0 +} + +// isPath reports whether c is a (non-punctuation) path byte. +func isPath(c byte) bool { + // mask is a 128-bit bitmap with 1s for allowed bytes, + // so that the byte c can be tested with a shift and an and. + // If c > 128, then 1<>64)) != 0 +} + +// isName reports whether s is a capitalized Go identifier (like Name). +func isName(s string) bool { + t, ok := ident(s) + if !ok || t != s { + return false + } + r, _ := utf8.DecodeRuneInString(s) + return unicode.IsUpper(r) +} + +// ident checks whether s begins with a Go identifier. +// If so, it returns the identifier, which is a prefix of s, and ok == true. +// Otherwise it returns "", false. +// The caller should skip over the first len(id) bytes of s +// before further processing. +func ident(s string) (id string, ok bool) { + // Scan [\pL_][\pL_0-9]* + n := 0 + for n < len(s) { + if c := s[n]; c < utf8.RuneSelf { + if isIdentASCII(c) && (n > 0 || c < '0' || c > '9') { + n++ + continue + } + break + } + r, nr := utf8.DecodeRuneInString(s[n:]) + if unicode.IsLetter(r) { + n += nr + continue + } + break + } + return s[:n], n > 0 +} + +// isIdentASCII reports whether c is an ASCII identifier byte. +func isIdentASCII(c byte) bool { + // mask is a 128-bit bitmap with 1s for allowed bytes, + // so that the byte c can be tested with a shift and an and. + // If c > 128, then 1<>64)) != 0 +} + +// validImportPath reports whether path is a valid import path. +// It is a lightly edited copy of golang.org/x/mod/module.CheckImportPath. +func validImportPath(path string) bool { + if !utf8.ValidString(path) { + return false + } + if path == "" { + return false + } + if path[0] == '-' { + return false + } + if strings.Contains(path, "//") { + return false + } + if path[len(path)-1] == '/' { + return false + } + elemStart := 0 + for i, r := range path { + if r == '/' { + if !validImportPathElem(path[elemStart:i]) { + return false + } + elemStart = i + 1 + } + } + return validImportPathElem(path[elemStart:]) +} + +func validImportPathElem(elem string) bool { + if elem == "" || elem[0] == '.' || elem[len(elem)-1] == '.' { + return false + } + for i := 0; i < len(elem); i++ { + if !importPathOK(elem[i]) { + return false + } + } + return true +} + +func importPathOK(c byte) bool { + // mask is a 128-bit bitmap with 1s for allowed bytes, + // so that the byte c can be tested with a shift and an and. + // If c > 128, then 1<>64)) != 0 +} diff --git a/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/print.go b/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/print.go new file mode 100644 index 00000000..4e9da3d1 --- /dev/null +++ b/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/print.go @@ -0,0 +1,290 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package comment + +import ( + "bytes" + "fmt" + "strings" +) + +// A Printer is a doc comment printer. +// The fields in the struct can be filled in before calling +// any of the printing methods +// in order to customize the details of the printing process. +type Printer struct { + // HeadingLevel is the nesting level used for + // HTML and Markdown headings. + // If HeadingLevel is zero, it defaults to level 3, + // meaning to use

and ###. + HeadingLevel int + + // HeadingID is a function that computes the heading ID + // (anchor tag) to use for the heading h when generating + // HTML and Markdown. If HeadingID returns an empty string, + // then the heading ID is omitted. + // If HeadingID is nil, h.DefaultID is used. + HeadingID func(h *Heading) string + + // DocLinkURL is a function that computes the URL for the given DocLink. + // If DocLinkURL is nil, then link.DefaultURL(p.DocLinkBaseURL) is used. + DocLinkURL func(link *DocLink) string + + // DocLinkBaseURL is used when DocLinkURL is nil, + // passed to [DocLink.DefaultURL] to construct a DocLink's URL. + // See that method's documentation for details. + DocLinkBaseURL string + + // TextPrefix is a prefix to print at the start of every line + // when generating text output using the Text method. + TextPrefix string + + // TextCodePrefix is the prefix to print at the start of each + // preformatted (code block) line when generating text output, + // instead of (not in addition to) TextPrefix. + // If TextCodePrefix is the empty string, it defaults to TextPrefix+"\t". + TextCodePrefix string + + // TextWidth is the maximum width text line to generate, + // measured in Unicode code points, + // excluding TextPrefix and the newline character. + // If TextWidth is zero, it defaults to 80 minus the number of code points in TextPrefix. + // If TextWidth is negative, there is no limit. + TextWidth int +} + +func (p *Printer) headingLevel() int { + if p.HeadingLevel <= 0 { + return 3 + } + return p.HeadingLevel +} + +func (p *Printer) headingID(h *Heading) string { + if p.HeadingID == nil { + return h.DefaultID() + } + return p.HeadingID(h) +} + +func (p *Printer) docLinkURL(link *DocLink) string { + if p.DocLinkURL != nil { + return p.DocLinkURL(link) + } + return link.DefaultURL(p.DocLinkBaseURL) +} + +// DefaultURL constructs and returns the documentation URL for l, +// using baseURL as a prefix for links to other packages. +// +// The possible forms returned by DefaultURL are: +// - baseURL/ImportPath, for a link to another package +// - baseURL/ImportPath#Name, for a link to a const, func, type, or var in another package +// - baseURL/ImportPath#Recv.Name, for a link to a method in another package +// - #Name, for a link to a const, func, type, or var in this package +// - #Recv.Name, for a link to a method in this package +// +// If baseURL ends in a trailing slash, then DefaultURL inserts +// a slash between ImportPath and # in the anchored forms. +// For example, here are some baseURL values and URLs they can generate: +// +// "/pkg/" → "/pkg/math/#Sqrt" +// "/pkg" → "/pkg/math#Sqrt" +// "/" → "/math/#Sqrt" +// "" → "/math#Sqrt" +func (l *DocLink) DefaultURL(baseURL string) string { + if l.ImportPath != "" { + slash := "" + if strings.HasSuffix(baseURL, "/") { + slash = "/" + } else { + baseURL += "/" + } + switch { + case l.Name == "": + return baseURL + l.ImportPath + slash + case l.Recv != "": + return baseURL + l.ImportPath + slash + "#" + l.Recv + "." + l.Name + default: + return baseURL + l.ImportPath + slash + "#" + l.Name + } + } + if l.Recv != "" { + return "#" + l.Recv + "." + l.Name + } + return "#" + l.Name +} + +// DefaultID returns the default anchor ID for the heading h. +// +// The default anchor ID is constructed by converting every +// rune that is not alphanumeric ASCII to an underscore +// and then adding the prefix “hdr-”. +// For example, if the heading text is “Go Doc Comments”, +// the default ID is “hdr-Go_Doc_Comments”. +func (h *Heading) DefaultID() string { + // Note: The “hdr-” prefix is important to avoid DOM clobbering attacks. + // See https://pkg.go.dev/github.com/google/safehtml#Identifier. + var out strings.Builder + var p textPrinter + p.oneLongLine(&out, h.Text) + s := strings.TrimSpace(out.String()) + if s == "" { + return "" + } + out.Reset() + out.WriteString("hdr-") + for _, r := range s { + if r < 0x80 && isIdentASCII(byte(r)) { + out.WriteByte(byte(r)) + } else { + out.WriteByte('_') + } + } + return out.String() +} + +type commentPrinter struct { + *Printer + headingPrefix string + needDoc map[string]bool +} + +// Comment returns the standard Go formatting of the Doc, +// without any comment markers. +func (p *Printer) Comment(d *Doc) []byte { + cp := &commentPrinter{Printer: p} + var out bytes.Buffer + for i, x := range d.Content { + if i > 0 && blankBefore(x) { + out.WriteString("\n") + } + cp.block(&out, x) + } + + // Print one block containing all the link definitions that were used, + // and then a second block containing all the unused ones. + // This makes it easy to clean up the unused ones: gofmt and + // delete the final block. And it's a nice visual signal without + // affecting the way the comment formats for users. + for i := 0; i < 2; i++ { + used := i == 0 + first := true + for _, def := range d.Links { + if def.Used == used { + if first { + out.WriteString("\n") + first = false + } + out.WriteString("[") + out.WriteString(def.Text) + out.WriteString("]: ") + out.WriteString(def.URL) + out.WriteString("\n") + } + } + } + + return out.Bytes() +} + +// blankBefore reports whether the block x requires a blank line before it. +// All blocks do, except for Lists that return false from x.BlankBefore(). +func blankBefore(x Block) bool { + if x, ok := x.(*List); ok { + return x.BlankBefore() + } + return true +} + +// block prints the block x to out. +func (p *commentPrinter) block(out *bytes.Buffer, x Block) { + switch x := x.(type) { + default: + fmt.Fprintf(out, "?%T", x) + + case *Paragraph: + p.text(out, "", x.Text) + out.WriteString("\n") + + case *Heading: + out.WriteString("# ") + p.text(out, "", x.Text) + out.WriteString("\n") + + case *Code: + md := x.Text + for md != "" { + var line string + line, md, _ = strings.Cut(md, "\n") + if line != "" { + out.WriteString("\t") + out.WriteString(line) + } + out.WriteString("\n") + } + + case *List: + loose := x.BlankBetween() + for i, item := range x.Items { + if i > 0 && loose { + out.WriteString("\n") + } + out.WriteString(" ") + if item.Number == "" { + out.WriteString(" - ") + } else { + out.WriteString(item.Number) + out.WriteString(". ") + } + for i, blk := range item.Content { + const fourSpace = " " + if i > 0 { + out.WriteString("\n" + fourSpace) + } + p.text(out, fourSpace, blk.(*Paragraph).Text) + out.WriteString("\n") + } + } + } +} + +// text prints the text sequence x to out. +func (p *commentPrinter) text(out *bytes.Buffer, indent string, x []Text) { + for _, t := range x { + switch t := t.(type) { + case Plain: + p.indent(out, indent, string(t)) + case Italic: + p.indent(out, indent, string(t)) + case *Link: + if t.Auto { + p.text(out, indent, t.Text) + } else { + out.WriteString("[") + p.text(out, indent, t.Text) + out.WriteString("]") + } + case *DocLink: + out.WriteString("[") + p.text(out, indent, t.Text) + out.WriteString("]") + } + } +} + +// indent prints s to out, indenting with the indent string +// after each newline in s. +func (p *commentPrinter) indent(out *bytes.Buffer, indent, s string) { + for s != "" { + line, rest, ok := strings.Cut(s, "\n") + out.WriteString(line) + if ok { + out.WriteString("\n") + out.WriteString(indent) + } + s = rest + } +} diff --git a/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/std.go b/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/std.go new file mode 100644 index 00000000..6786e4d7 --- /dev/null +++ b/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/std.go @@ -0,0 +1,44 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Code generated by 'go generate' DO NOT EDIT. +//disabled go:generate ./mkstd.sh + +package comment + +var stdPkgs = []string{ + "bufio", + "bytes", + "context", + "crypto", + "embed", + "encoding", + "errors", + "expvar", + "flag", + "fmt", + "hash", + "html", + "image", + "io", + "log", + "math", + "mime", + "net", + "os", + "path", + "plugin", + "reflect", + "regexp", + "runtime", + "sort", + "strconv", + "strings", + "sync", + "syscall", + "testing", + "time", + "unicode", + "unsafe", +} diff --git a/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/text.go b/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/text.go new file mode 100644 index 00000000..6f9c2e20 --- /dev/null +++ b/vendor/mvdan.cc/gofumpt/internal/govendor/go/doc/comment/text.go @@ -0,0 +1,337 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package comment + +import ( + "bytes" + "fmt" + "sort" + "strings" + "unicode/utf8" +) + +// A textPrinter holds the state needed for printing a Doc as plain text. +type textPrinter struct { + *Printer + long strings.Builder + prefix string + codePrefix string + width int +} + +// 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{ + Printer: p, + prefix: p.TextPrefix, + codePrefix: p.TextCodePrefix, + width: p.TextWidth, + } + if tp.codePrefix == "" { + tp.codePrefix = p.TextPrefix + "\t" + } + if tp.width == 0 { + tp.width = 80 - utf8.RuneCountInString(tp.prefix) + } + + var out bytes.Buffer + for i, x := range d.Content { + if i > 0 && blankBefore(x) { + out.WriteString(tp.prefix) + writeNL(&out) + } + tp.block(&out, x) + } + anyUsed := false + for _, def := range d.Links { + if def.Used { + anyUsed = true + break + } + } + if anyUsed { + writeNL(&out) + for _, def := range d.Links { + if def.Used { + fmt.Fprintf(&out, "[%s]: %s\n", def.Text, def.URL) + } + } + } + return out.Bytes() +} + +// writeNL calls out.WriteByte('\n') +// but first trims trailing spaces on the previous line. +func writeNL(out *bytes.Buffer) { + // Trim trailing spaces. + data := out.Bytes() + n := 0 + for n < len(data) && (data[len(data)-n-1] == ' ' || data[len(data)-n-1] == '\t') { + n++ + } + if n > 0 { + out.Truncate(len(data) - n) + } + out.WriteByte('\n') +} + +// block prints the block x to out. +func (p *textPrinter) block(out *bytes.Buffer, x Block) { + switch x := x.(type) { + default: + fmt.Fprintf(out, "?%T\n", x) + + case *Paragraph: + out.WriteString(p.prefix) + p.text(out, "", x.Text) + + case *Heading: + out.WriteString(p.prefix) + out.WriteString("# ") + p.text(out, "", x.Text) + + case *Code: + text := x.Text + for text != "" { + var line string + line, text, _ = strings.Cut(text, "\n") + if line != "" { + out.WriteString(p.codePrefix) + out.WriteString(line) + } + writeNL(out) + } + + case *List: + loose := x.BlankBetween() + for i, item := range x.Items { + if i > 0 && loose { + out.WriteString(p.prefix) + writeNL(out) + } + out.WriteString(p.prefix) + out.WriteString(" ") + if item.Number == "" { + out.WriteString(" - ") + } else { + out.WriteString(item.Number) + out.WriteString(". ") + } + for i, blk := range item.Content { + const fourSpace = " " + if i > 0 { + writeNL(out) + out.WriteString(p.prefix) + out.WriteString(fourSpace) + } + p.text(out, fourSpace, blk.(*Paragraph).Text) + } + } + } +} + +// text prints the text sequence x to out. +func (p *textPrinter) text(out *bytes.Buffer, indent string, x []Text) { + p.oneLongLine(&p.long, x) + words := strings.Fields(p.long.String()) + p.long.Reset() + + var seq []int + if p.width < 0 || len(words) == 0 { + seq = []int{0, len(words)} // one long line + } else { + seq = wrap(words, p.width-utf8.RuneCountInString(indent)) + } + for i := 0; i+1 < len(seq); i++ { + if i > 0 { + out.WriteString(p.prefix) + out.WriteString(indent) + } + for j, w := range words[seq[i]:seq[i+1]] { + if j > 0 { + out.WriteString(" ") + } + out.WriteString(w) + } + writeNL(out) + } +} + +// oneLongLine prints the text sequence x to out as one long line, +// without worrying about line wrapping. +// Explicit links have the [ ] dropped to improve readability. +func (p *textPrinter) oneLongLine(out *strings.Builder, x []Text) { + for _, t := range x { + switch t := t.(type) { + case Plain: + out.WriteString(string(t)) + case Italic: + out.WriteString(string(t)) + case *Link: + p.oneLongLine(out, t.Text) + case *DocLink: + p.oneLongLine(out, t.Text) + } + } +} + +// wrap wraps words into lines of at most max runes, +// minimizing the sum of the squares of the leftover lengths +// at the end of each line (except the last, of course), +// with a preference for ending lines at punctuation (.,:;). +// +// The returned slice gives the indexes of the first words +// on each line in the wrapped text with a final entry of len(words). +// Thus the lines are words[seq[0]:seq[1]], words[seq[1]:seq[2]], +// ..., words[seq[len(seq)-2]:seq[len(seq)-1]]. +// +// The implementation runs in O(n log n) time, where n = len(words), +// using the algorithm described in D. S. Hirschberg and L. L. Larmore, +// “[The least weight subsequence problem],” FOCS 1985, pp. 137-143. +// +// [The least weight subsequence problem]: https://doi.org/10.1109/SFCS.1985.60 +func wrap(words []string, max int) (seq []int) { + // The algorithm requires that our scoring function be concave, + // meaning that for all i₀ ≤ i₁ < j₀ ≤ j₁, + // weight(i₀, j₀) + weight(i₁, j₁) ≤ weight(i₀, j₁) + weight(i₁, j₀). + // + // Our weights are two-element pairs [hi, lo] + // ordered by elementwise comparison. + // The hi entry counts the weight for lines that are longer than max, + // and the lo entry counts the weight for lines that are not. + // This forces the algorithm to first minimize the number of lines + // that are longer than max, which correspond to lines with + // single very long words. Having done that, it can move on to + // minimizing the lo score, which is more interesting. + // + // The lo score is the sum for each line of the square of the + // number of spaces remaining at the end of the line and a + // penalty of 64 given out for not ending the line in a + // punctuation character (.,:;). + // The penalty is somewhat arbitrarily chosen by trying + // different amounts and judging how nice the wrapped text looks. + // Roughly speaking, using 64 means that we are willing to + // end a line with eight blank spaces in order to end at a + // punctuation character, even if the next word would fit in + // those spaces. + // + // We care about ending in punctuation characters because + // it makes the text easier to skim if not too many sentences + // or phrases begin with a single word on the previous line. + + // A score is the score (also called weight) for a given line. + // add and cmp add and compare scores. + type score struct { + hi int64 + lo int64 + } + add := func(s, t score) score { return score{s.hi + t.hi, s.lo + t.lo} } + cmp := func(s, t score) int { + switch { + case s.hi < t.hi: + return -1 + case s.hi > t.hi: + return +1 + case s.lo < t.lo: + return -1 + case s.lo > t.lo: + return +1 + } + return 0 + } + + // total[j] is the total number of runes + // (including separating spaces) in words[:j]. + total := make([]int, len(words)+1) + total[0] = 0 + for i, s := range words { + total[1+i] = total[i] + utf8.RuneCountInString(s) + 1 + } + + // weight returns weight(i, j). + weight := func(i, j int) score { + // On the last line, there is zero weight for being too short. + n := total[j] - 1 - total[i] + if j == len(words) && n <= max { + return score{0, 0} + } + + // Otherwise the weight is the penalty plus the square of the number of + // characters remaining on the line or by which the line goes over. + // In the latter case, that value goes in the hi part of the score. + // (See note above.) + p := wrapPenalty(words[j-1]) + v := int64(max-n) * int64(max-n) + if n > max { + return score{v, p} + } + return score{0, v + p} + } + + // The rest of this function is “The Basic Algorithm” from + // Hirschberg and Larmore's conference paper, + // using the same names as in the paper. + f := []score{{0, 0}} + g := func(i, j int) score { return add(f[i], weight(i, j)) } + + bridge := func(a, b, c int) bool { + k := c + sort.Search(len(words)+1-c, func(k int) bool { + k += c + return cmp(g(a, k), g(b, k)) > 0 + }) + if k > len(words) { + return true + } + return cmp(g(c, k), g(b, k)) <= 0 + } + + // d is a one-ended deque implemented as a slice. + d := make([]int, 1, len(words)) + d[0] = 0 + bestleft := make([]int, 1, len(words)) + bestleft[0] = -1 + for m := 1; m < len(words); m++ { + f = append(f, g(d[0], m)) + bestleft = append(bestleft, d[0]) + for len(d) > 1 && cmp(g(d[1], m+1), g(d[0], m+1)) <= 0 { + d = d[1:] // “Retire” + } + for len(d) > 1 && bridge(d[len(d)-2], d[len(d)-1], m) { + d = d[:len(d)-1] // “Fire” + } + if cmp(g(m, len(words)), g(d[len(d)-1], len(words))) < 0 { + d = append(d, m) // “Hire” + // The next few lines are not in the paper but are necessary + // to handle two-word inputs correctly. It appears to be + // just a bug in the paper's pseudocode. + if len(d) == 2 && cmp(g(d[1], m+1), g(d[0], m+1)) <= 0 { + d = d[1:] + } + } + } + bestleft = append(bestleft, d[0]) + + // Recover least weight sequence from bestleft. + n := 1 + for m := len(words); m > 0; m = bestleft[m] { + n++ + } + seq = make([]int, n) + for m := len(words); m > 0; m = bestleft[m] { + n-- + seq[n] = m + } + return seq +} + +// wrapPenalty is the penalty for inserting a line break after word s. +func wrapPenalty(s string) int64 { + switch s[len(s)-1] { + case '.', ',', ':', ';': + return 0 + } + return 64 +} diff --git a/vendor/mvdan.cc/gofumpt/internal/govendor/go/format/format.go b/vendor/mvdan.cc/gofumpt/internal/govendor/go/format/format.go new file mode 100644 index 00000000..5540686e --- /dev/null +++ b/vendor/mvdan.cc/gofumpt/internal/govendor/go/format/format.go @@ -0,0 +1,134 @@ +// Copyright 2012 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 format implements standard formatting of Go source. +// +// Note that formatting of Go source code changes over time, so tools relying on +// consistent formatting should execute a specific version of the gofmt binary +// instead of using this package. That way, the formatting will be stable, and +// the tools won't need to be recompiled each time gofmt changes. +// +// For example, pre-submit checks that use this package directly would behave +// differently depending on what Go version each developer uses, causing the +// check to be inherently fragile. +package format + +import ( + "bytes" + "fmt" + "go/ast" + "go/parser" + "go/token" + "io" + + "mvdan.cc/gofumpt/internal/govendor/go/printer" +) + +// Keep these in sync with cmd/gofmt/gofmt.go. +const ( + tabWidth = 8 + printerMode = printer.UseSpaces | printer.TabIndent | printerNormalizeNumbers + + // printerNormalizeNumbers means to canonicalize number literal prefixes + // and exponents while printing. See https://golang.org/doc/go1.13#gofmt. + // + // This value is defined in mvdan.cc/gofumpt/internal/govendor/go/printer specifically for mvdan.cc/gofumpt/internal/govendor/go/format and cmd/gofmt. + printerNormalizeNumbers = 1 << 30 +) + +var config = printer.Config{Mode: printerMode, Tabwidth: tabWidth} + +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 +// nodes representing partial source files (for instance, if the node is +// 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. +func Node(dst io.Writer, fset *token.FileSet, node any) error { + // Determine if we have a complete source file (file != nil). + var file *ast.File + var cnode *printer.CommentedNode + switch n := node.(type) { + case *ast.File: + file = n + case *printer.CommentedNode: + if f, ok := n.Node.(*ast.File); ok { + file = f + cnode = n + } + } + + // Sort imports if necessary. + if file != nil && hasUnsortedImports(file) { + // Make a copy of the AST because ast.SortImports is destructive. + // TODO(gri) Do this more efficiently. + var buf bytes.Buffer + err := config.Fprint(&buf, fset, file) + if err != nil { + return err + } + file, err = parser.ParseFile(fset, "", buf.Bytes(), parserMode) + if err != nil { + // We should never get here. If we do, provide good diagnostic. + return fmt.Errorf("format.Node internal error (%s)", err) + } + ast.SortImports(fset, file) + + // Use new file with sorted imports. + node = file + if cnode != nil { + node = &printer.CommentedNode{Node: file, Comments: cnode.Comments} + } + } + + return config.Fprint(dst, fset, node) +} + +// Source formats src in canonical gofmt style and returns the result +// or an (I/O or syntax) error. src is expected to be a syntactically +// correct Go source file, or a list of Go declarations or statements. +// +// If src is a partial source file, the leading and trailing space of src +// is applied to the result (such that it has the same leading and trailing +// space as src), and the result is indented by the same amount as the first +// line of src containing code. Imports are not sorted for partial source files. +func Source(src []byte) ([]byte, error) { + fset := token.NewFileSet() + file, sourceAdj, indentAdj, err := parse(fset, "", src, true) + if err != nil { + return nil, err + } + + if sourceAdj == nil { + // Complete source file. + // TODO(gri) consider doing this always. + ast.SortImports(fset, file) + } + + return format(fset, file, sourceAdj, indentAdj, src, config) +} + +func hasUnsortedImports(file *ast.File) bool { + for _, d := range file.Decls { + d, ok := d.(*ast.GenDecl) + if !ok || d.Tok != token.IMPORT { + // Not an import declaration, so we're done. + // Imports are always first. + return false + } + if d.Lparen.IsValid() { + // For now assume all grouped imports are unsorted. + // TODO(gri) Should check if they are sorted already. + return true + } + // Ungrouped imports are sorted by default. + } + return false +} diff --git a/vendor/mvdan.cc/gofumpt/internal/govendor/go/format/internal.go b/vendor/mvdan.cc/gofumpt/internal/govendor/go/format/internal.go new file mode 100644 index 00000000..df035871 --- /dev/null +++ b/vendor/mvdan.cc/gofumpt/internal/govendor/go/format/internal.go @@ -0,0 +1,177 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// TODO(gri): This file and the file src/cmd/gofmt/internal.go are +// the same (but for this comment and the package name). Do not modify +// one without the other. Determine if we can factor out functionality +// in a public API. See also #11844 for context. + +package format + +import ( + "bytes" + "go/ast" + "go/parser" + "go/token" + "strings" + + "mvdan.cc/gofumpt/internal/govendor/go/printer" +) + +// parse parses src, which was read from the named file, +// as a Go source file, declaration, or statement list. +func parse(fset *token.FileSet, filename string, src []byte, fragmentOk bool) ( + file *ast.File, + sourceAdj func(src []byte, indent int) []byte, + indentAdj int, + err error, +) { + // Try as whole source file. + file, err = parser.ParseFile(fset, filename, src, parserMode) + // If there's no error, return. If the error is that the source file didn't begin with a + // package line and source fragments are ok, fall through to + // try as a source fragment. Stop and return on any other error. + if err == nil || !fragmentOk || !strings.Contains(err.Error(), "expected 'package'") { + return + } + + // If this is a declaration list, make it a source file + // by inserting a package clause. + // Insert using a ';', not a newline, so that the line numbers + // in psrc match the ones in src. + psrc := append([]byte("package p;"), src...) + file, err = parser.ParseFile(fset, filename, psrc, parserMode) + if err == nil { + sourceAdj = func(src []byte, indent int) []byte { + // Remove the package clause. + // Gofmt has turned the ';' into a '\n'. + src = src[indent+len("package p\n"):] + return bytes.TrimSpace(src) + } + return + } + // If the error is that the source file didn't begin with a + // declaration, fall through to try as a statement list. + // Stop and return on any other error. + if !strings.Contains(err.Error(), "expected declaration") { + return + } + + // If this is a statement list, make it a source file + // by inserting a package clause and turning the list + // into a function body. This handles expressions too. + // Insert using a ';', not a newline, so that the line numbers + // in fsrc match the ones in src. Add an extra '\n' before the '}' + // to make sure comments are flushed before the '}'. + fsrc := append(append([]byte("package p; func _() {"), src...), '\n', '\n', '}') + file, err = parser.ParseFile(fset, filename, fsrc, parserMode) + if err == nil { + sourceAdj = func(src []byte, indent int) []byte { + // Cap adjusted indent to zero. + if indent < 0 { + indent = 0 + } + // Remove the wrapping. + // Gofmt has turned the "; " into a "\n\n". + // There will be two non-blank lines with indent, hence 2*indent. + src = src[2*indent+len("package p\n\nfunc _() {"):] + // Remove only the "}\n" suffix: remaining whitespaces will be trimmed anyway + src = src[:len(src)-len("}\n")] + return bytes.TrimSpace(src) + } + // Gofmt has also indented the function body one level. + // Adjust that with indentAdj. + indentAdj = -1 + } + + // Succeeded, or out of options. + return +} + +// format formats the given package file originally obtained from src +// and adjusts the result based on the original source via sourceAdj +// and indentAdj. +func format( + fset *token.FileSet, + file *ast.File, + sourceAdj func(src []byte, indent int) []byte, + indentAdj int, + src []byte, + cfg printer.Config, +) ([]byte, error) { + if sourceAdj == nil { + // Complete source file. + var buf bytes.Buffer + err := cfg.Fprint(&buf, fset, file) + if err != nil { + return nil, err + } + return buf.Bytes(), nil + } + + // Partial source file. + // Determine and prepend leading space. + i, j := 0, 0 + for j < len(src) && isSpace(src[j]) { + if src[j] == '\n' { + i = j + 1 // byte offset of last line in leading space + } + j++ + } + var res []byte + res = append(res, src[:i]...) + + // Determine and prepend indentation of first code line. + // Spaces are ignored unless there are no tabs, + // in which case spaces count as one tab. + indent := 0 + hasSpace := false + for _, b := range src[i:j] { + switch b { + case ' ': + hasSpace = true + case '\t': + indent++ + } + } + if indent == 0 && hasSpace { + indent = 1 + } + for i := 0; i < indent; i++ { + res = append(res, '\t') + } + + // Format the source. + // Write it without any leading and trailing space. + cfg.Indent = indent + indentAdj + var buf bytes.Buffer + err := cfg.Fprint(&buf, fset, file) + if err != nil { + return nil, err + } + out := sourceAdj(buf.Bytes(), cfg.Indent) + + // If the adjusted output is empty, the source + // was empty but (possibly) for white space. + // The result is the incoming source. + if len(out) == 0 { + return src, nil + } + + // Otherwise, append output to leading space. + res = append(res, out...) + + // Determine and append trailing space. + i = len(src) + for i > 0 && isSpace(src[i-1]) { + i-- + } + return append(res, src[i:]...), nil +} + +// isSpace reports whether the byte is a space character. +// isSpace defines a space as being among the following bytes: ' ', '\t', '\n' and '\r'. +func isSpace(b byte) bool { + return b == ' ' || b == '\t' || b == '\n' || b == '\r' +} diff --git a/vendor/mvdan.cc/gofumpt/internal/govendor/go/printer/comment.go b/vendor/mvdan.cc/gofumpt/internal/govendor/go/printer/comment.go new file mode 100644 index 00000000..5b9775dc --- /dev/null +++ b/vendor/mvdan.cc/gofumpt/internal/govendor/go/printer/comment.go @@ -0,0 +1,155 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package printer + +import ( + "go/ast" + "strings" + + "mvdan.cc/gofumpt/internal/govendor/go/doc/comment" +) + +// formatDocComment reformats the doc comment list, +// returning the canonical formatting. +func formatDocComment(list []*ast.Comment) []*ast.Comment { + // Extract comment text (removing comment markers). + var kind, text string + var directives []*ast.Comment + if len(list) == 1 && strings.HasPrefix(list[0].Text, "/*") { + kind = "/*" + text = list[0].Text + if !strings.Contains(text, "\n") || allStars(text) { + // Single-line /* .. */ comment in doc comment position, + // or multiline old-style comment like + // /* + // * Comment + // * text here. + // */ + // Should not happen, since it will not work well as a + // doc comment, but if it does, just ignore: + // reformatting it will only make the situation worse. + return list + } + text = text[2 : len(text)-2] // cut /* and */ + } else if strings.HasPrefix(list[0].Text, "//") { + kind = "//" + var b strings.Builder + for _, c := range list { + if !strings.HasPrefix(c.Text, "//") { + return list + } + // Accumulate //go:build etc lines separately. + if isDirective(c.Text[2:]) { + directives = append(directives, c) + continue + } + b.WriteString(strings.TrimPrefix(c.Text[2:], " ")) + b.WriteString("\n") + } + text = b.String() + } else { + // Not sure what this is, so leave alone. + return list + } + + if text == "" { + return list + } + + // Parse comment and reformat as text. + var p comment.Parser + d := p.Parse(text) + + var pr comment.Printer + text = string(pr.Comment(d)) + + // For /* */ comment, return one big comment with text inside. + slash := list[0].Slash + if kind == "/*" { + c := &ast.Comment{ + Slash: slash, + Text: "/*\n" + text + "*/", + } + return []*ast.Comment{c} + } + + // For // comment, return sequence of // lines. + var out []*ast.Comment + for text != "" { + var line string + line, text, _ = strings.Cut(text, "\n") + if line == "" { + line = "//" + } else if strings.HasPrefix(line, "\t") { + line = "//" + line + } else { + line = "// " + line + } + out = append(out, &ast.Comment{ + Slash: slash, + Text: line, + }) + } + if len(directives) > 0 { + out = append(out, &ast.Comment{ + Slash: slash, + Text: "//", + }) + for _, c := range directives { + out = append(out, &ast.Comment{ + Slash: slash, + Text: c.Text, + }) + } + } + return out +} + +// isDirective reports whether c is a comment directive. +// See go.dev/issue/37974. +// This code is also in go/ast. +func isDirective(c string) bool { + // "//line " is a line directive. + // "//extern " is for gccgo. + // "//export " is for cgo. + // (The // has been removed.) + if strings.HasPrefix(c, "line ") || strings.HasPrefix(c, "extern ") || strings.HasPrefix(c, "export ") { + return true + } + + // "//[a-z0-9]+:[a-z0-9]" + // (The // has been removed.) + colon := strings.Index(c, ":") + if colon <= 0 || colon+1 >= len(c) { + return false + } + for i := 0; i <= colon+1; i++ { + if i == colon { + continue + } + b := c[i] + if !('a' <= b && b <= 'z' || '0' <= b && b <= '9') { + return false + } + } + return true +} + +// allStars reports whether text is the interior of an +// old-style /* */ comment with a star at the start of each line. +func allStars(text string) bool { + for i := 0; i < len(text); i++ { + if text[i] == '\n' { + j := i + 1 + for j < len(text) && (text[j] == ' ' || text[j] == '\t') { + j++ + } + if j < len(text) && text[j] != '*' { + return false + } + } + } + return true +} diff --git a/vendor/mvdan.cc/gofumpt/internal/govendor/go/printer/gobuild.go b/vendor/mvdan.cc/gofumpt/internal/govendor/go/printer/gobuild.go new file mode 100644 index 00000000..f00492d0 --- /dev/null +++ b/vendor/mvdan.cc/gofumpt/internal/govendor/go/printer/gobuild.go @@ -0,0 +1,170 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package printer + +import ( + "go/build/constraint" + "sort" + "text/tabwriter" +) + +func (p *printer) fixGoBuildLines() { + if len(p.goBuild)+len(p.plusBuild) == 0 { + return + } + + // Find latest possible placement of //go:build and // +build comments. + // That's just after the last blank line before we find a non-comment. + // (We'll add another blank line after our comment block.) + // When we start dropping // +build comments, we can skip over /* */ comments too. + // Note that we are processing tabwriter input, so every comment + // begins and ends with a tabwriter.Escape byte. + // And some newlines have turned into \f bytes. + insert := 0 + for pos := 0; ; { + // Skip leading space at beginning of line. + blank := true + for pos < len(p.output) && (p.output[pos] == ' ' || p.output[pos] == '\t') { + pos++ + } + // Skip over // comment if any. + if pos+3 < len(p.output) && p.output[pos] == tabwriter.Escape && p.output[pos+1] == '/' && p.output[pos+2] == '/' { + blank = false + for pos < len(p.output) && !isNL(p.output[pos]) { + pos++ + } + } + // Skip over \n at end of line. + if pos >= len(p.output) || !isNL(p.output[pos]) { + break + } + pos++ + + if blank { + insert = pos + } + } + + // If there is a //go:build comment before the place we identified, + // use that point instead. (Earlier in the file is always fine.) + if len(p.goBuild) > 0 && p.goBuild[0] < insert { + insert = p.goBuild[0] + } else if len(p.plusBuild) > 0 && p.plusBuild[0] < insert { + insert = p.plusBuild[0] + } + + var x constraint.Expr + switch len(p.goBuild) { + case 0: + // Synthesize //go:build expression from // +build lines. + for _, pos := range p.plusBuild { + y, err := constraint.Parse(p.commentTextAt(pos)) + if err != nil { + x = nil + break + } + if x == nil { + x = y + } else { + x = &constraint.AndExpr{X: x, Y: y} + } + } + case 1: + // Parse //go:build expression. + x, _ = constraint.Parse(p.commentTextAt(p.goBuild[0])) + } + + var block []byte + if x == nil { + // Don't have a valid //go:build expression to treat as truth. + // Bring all the lines together but leave them alone. + // Note that these are already tabwriter-escaped. + for _, pos := range p.goBuild { + block = append(block, p.lineAt(pos)...) + } + for _, pos := range p.plusBuild { + block = append(block, p.lineAt(pos)...) + } + } else { + block = append(block, tabwriter.Escape) + block = append(block, "//go:build "...) + block = append(block, x.String()...) + block = append(block, tabwriter.Escape, '\n') + if len(p.plusBuild) > 0 { + lines, err := constraint.PlusBuildLines(x) + if err != nil { + lines = []string{"// +build error: " + err.Error()} + } + for _, line := range lines { + block = append(block, tabwriter.Escape) + block = append(block, line...) + block = append(block, tabwriter.Escape, '\n') + } + } + } + block = append(block, '\n') + + // Build sorted list of lines to delete from remainder of output. + toDelete := append(p.goBuild, p.plusBuild...) + sort.Ints(toDelete) + + // Collect output after insertion point, with lines deleted, into after. + var after []byte + start := insert + for _, end := range toDelete { + if end < start { + continue + } + after = appendLines(after, p.output[start:end]) + start = end + len(p.lineAt(end)) + } + after = appendLines(after, p.output[start:]) + if n := len(after); n >= 2 && isNL(after[n-1]) && isNL(after[n-2]) { + after = after[:n-1] + } + + p.output = p.output[:insert] + p.output = append(p.output, block...) + p.output = append(p.output, after...) +} + +// appendLines is like append(x, y...) +// but it avoids creating doubled blank lines, +// which would not be gofmt-standard output. +// It assumes that only whole blocks of lines are being appended, +// not line fragments. +func appendLines(x, y []byte) []byte { + if len(y) > 0 && isNL(y[0]) && // y starts in blank line + (len(x) == 0 || len(x) >= 2 && isNL(x[len(x)-1]) && isNL(x[len(x)-2])) { // x is empty or ends in blank line + y = y[1:] // delete y's leading blank line + } + return append(x, y...) +} + +func (p *printer) lineAt(start int) []byte { + pos := start + for pos < len(p.output) && !isNL(p.output[pos]) { + pos++ + } + if pos < len(p.output) { + pos++ + } + return p.output[start:pos] +} + +func (p *printer) commentTextAt(start int) string { + if start < len(p.output) && p.output[start] == tabwriter.Escape { + start++ + } + pos := start + for pos < len(p.output) && p.output[pos] != tabwriter.Escape && !isNL(p.output[pos]) { + pos++ + } + return string(p.output[start:pos]) +} + +func isNL(b byte) bool { + return b == '\n' || b == '\f' +} diff --git a/vendor/mvdan.cc/gofumpt/internal/govendor/go/printer/nodes.go b/vendor/mvdan.cc/gofumpt/internal/govendor/go/printer/nodes.go new file mode 100644 index 00000000..a58525b8 --- /dev/null +++ b/vendor/mvdan.cc/gofumpt/internal/govendor/go/printer/nodes.go @@ -0,0 +1,2001 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements printing of AST nodes; specifically +// expressions, statements, declarations, and files. It uses +// the print functionality implemented in printer.go. + +package printer + +import ( + "go/ast" + "go/token" + "math" + "strconv" + "strings" + "unicode" + "unicode/utf8" +) + +// Formatting issues: +// - better comment formatting for /*-style comments at the end of a line (e.g. a declaration) +// when the comment spans multiple lines; if such a comment is just two lines, formatting is +// not idempotent +// - formatting of expression lists +// - should use blank instead of tab to separate one-line function bodies from +// the function header unless there is a group of consecutive one-liners + +// ---------------------------------------------------------------------------- +// Common AST nodes. + +// Print as many newlines as necessary (but at least min newlines) to get to +// the current line. ws is printed before the first line break. If newSection +// is set, the first line break is printed as formfeed. Returns 0 if no line +// breaks were printed, returns 1 if there was exactly one newline printed, +// and returns a value > 1 if there was a formfeed or more than one newline +// printed. +// +// TODO(gri): linebreak may add too many lines if the next statement at "line" +// is preceded by comments because the computation of n assumes +// the current position before the comment and the target position +// after the comment. Thus, after interspersing such comments, the +// space taken up by them is not considered to reduce the number of +// 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 + } + if n > 0 { + p.print(ws) + if newSection { + p.print(formfeed) + n-- + nbreaks = 2 + } + nbreaks += n + for ; n > 0; n-- { + p.print(newline) + } + } + return +} + +// setComment sets g as the next comment if g != nil and if node comments +// are enabled - this mode is used when printing source code fragments such +// as exports only. It assumes that there is no pending comment in p.comments +// and at most one pending comment in the p.comment cache. +func (p *printer) setComment(g *ast.CommentGroup) { + if g == nil || !p.useNodeComments { + return + } + if p.comments == nil { + // initialize p.comments lazily + p.comments = make([]*ast.CommentGroup, 1) + } else if p.cindex < len(p.comments) { + // for some reason there are pending comments; this + // should never happen - handle gracefully and flush + // all comments up to g, ignore anything after that + p.flush(p.posFor(g.List[0].Pos()), token.ILLEGAL) + p.comments = p.comments[0:1] + // in debug mode, report error + p.internalError("setComment found pending comments") + } + p.comments[0] = g + p.cindex = 0 + // don't overwrite any pending comment in the p.comment cache + // (there may be a pending comment when a line comment is + // immediately followed by a lead comment with no other + // tokens between) + if p.commentOffset == infinity { + p.nextComment() // get comment ready for use + } +} + +type exprListMode uint + +const ( + commaTerm exprListMode = 1 << iota // list is optionally terminated by a comma + noIndent // no extra indentation in multi-line lists +) + +// If indent is set, a multi-line identifier list is indented after the +// first linebreak encountered. +func (p *printer) identList(list []*ast.Ident, indent bool) { + // convert into an expression list so we can re-use exprList formatting + xlist := make([]ast.Expr, len(list)) + for i, x := range list { + xlist[i] = x + } + var mode exprListMode + if !indent { + mode = noIndent + } + p.exprList(token.NoPos, xlist, 1, mode, token.NoPos, false) +} + +const filteredMsg = "contains filtered or unexported fields" + +// Print a list of expressions. If the list spans multiple +// source lines, the original line breaks are respected between +// expressions. +// +// TODO(gri) Consider rewriting this to be independent of []ast.Expr +// so that we can use the algorithm for any kind of list +// +// (e.g., pass list via a channel over which to range). +func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exprListMode, next0 token.Pos, isIncomplete bool) { + if len(list) == 0 { + if isIncomplete { + prev := p.posFor(prev0) + next := p.posFor(next0) + if prev.IsValid() && prev.Line == next.Line { + p.print("/* " + filteredMsg + " */") + } else { + p.print(newline) + p.print(indent, "// "+filteredMsg, unindent, newline) + } + } + return + } + + prev := p.posFor(prev0) + next := p.posFor(next0) + line := p.lineFor(list[0].Pos()) + endLine := p.lineFor(list[len(list)-1].End()) + + if prev.IsValid() && prev.Line == line && line == endLine { + // all list entries on a single line + for i, x := range list { + if i > 0 { + // use position of expression following the comma as + // comma position for correct comment placement + p.setPos(x.Pos()) + p.print(token.COMMA, blank) + } + p.expr0(x, depth) + } + if isIncomplete { + p.print(token.COMMA, blank, "/* "+filteredMsg+" */") + } + return + } + + // list entries span multiple lines; + // use source code positions to guide line breaks + + // Don't add extra indentation if noIndent is set; + // i.e., pretend that the first line is already indented. + ws := ignore + if mode&noIndent == 0 { + ws = indent + } + + // The first linebreak is always a formfeed since this section must not + // depend on any previous formatting. + prevBreak := -1 // index of last expression that was followed by a linebreak + if prev.IsValid() && prev.Line < line && p.linebreak(line, 0, ws, true) > 0 { + ws = ignore + prevBreak = 0 + } + + // initialize expression/key size: a zero value indicates expr/key doesn't fit on a single line + size := 0 + + // We use the ratio between the geometric mean of the previous key sizes and + // the current size to determine if there should be a break in the alignment. + // To compute the geometric mean we accumulate the ln(size) values (lnsum) + // and the number of sizes included (count). + lnsum := 0.0 + count := 0 + + // print all list elements + prevLine := prev.Line + for i, x := range list { + line = p.lineFor(x.Pos()) + + // Determine if the next linebreak, if any, needs to use formfeed: + // in general, use the entire node size to make the decision; for + // key:value expressions, use the key size. + // TODO(gri) for a better result, should probably incorporate both + // the key and the node size into the decision process + useFF := true + + // Determine element size: All bets are off if we don't have + // position information for the previous and next token (likely + // generated code - simply ignore the size in this case by setting + // it to 0). + prevSize := size + const infinity = 1e6 // larger than any source line + size = p.nodeSize(x, infinity) + pair, isPair := x.(*ast.KeyValueExpr) + if size <= infinity && prev.IsValid() && next.IsValid() { + // x fits on a single line + if isPair { + size = p.nodeSize(pair.Key, infinity) // size <= infinity + } + } else { + // size too large or we don't have good layout information + size = 0 + } + + // If the previous line and the current line had single- + // line-expressions and the key sizes are small or the + // ratio between the current key and the geometric mean + // if the previous key sizes does not exceed a threshold, + // align columns and do not use formfeed. + if prevSize > 0 && size > 0 { + const smallSize = 40 + if count == 0 || prevSize <= smallSize && size <= smallSize { + useFF = false + } else { + const r = 2.5 // threshold + geomean := math.Exp(lnsum / float64(count)) // count > 0 + ratio := float64(size) / geomean + useFF = r*ratio <= 1 || r <= ratio + } + } + + needsLinebreak := 0 < prevLine && prevLine < line + if i > 0 { + // Use position of expression following the comma as + // comma position for correct comment placement, but + // only if the expression is on the same line. + if !needsLinebreak { + p.setPos(x.Pos()) + } + p.print(token.COMMA) + needsBlank := true + if needsLinebreak { + // Lines are broken using newlines so comments remain aligned + // unless useFF is set or there are multiple expressions on + // the same line in which case formfeed is used. + nbreaks := p.linebreak(line, 0, ws, useFF || prevBreak+1 < i) + if nbreaks > 0 { + ws = ignore + prevBreak = i + needsBlank = false // we got a line break instead + } + // If there was a new section or more than one new line + // (which means that the tabwriter will implicitly break + // the section), reset the geomean variables since we are + // starting a new group of elements with the next element. + if nbreaks > 1 { + lnsum = 0 + count = 0 + } + } + if needsBlank { + p.print(blank) + } + } + + if len(list) > 1 && isPair && size > 0 && needsLinebreak { + // We have a key:value expression that fits onto one line + // and it's not on the same line as the prior expression: + // Use a column for the key such that consecutive entries + // can align if possible. + // (needsLinebreak is set if we started a new line before) + p.expr(pair.Key) + p.setPos(pair.Colon) + p.print(token.COLON, vtab) + p.expr(pair.Value) + } else { + p.expr0(x, depth) + } + + if size > 0 { + lnsum += math.Log(float64(size)) + count++ + } + + prevLine = line + } + + if mode&commaTerm != 0 && next.IsValid() && p.pos.Line < next.Line { + // Print a terminating comma if the next token is on a new line. + p.print(token.COMMA) + if isIncomplete { + p.print(newline) + p.print("// " + filteredMsg) + } + if ws == ignore && mode&noIndent == 0 { + // unindent if we indented + p.print(unindent) + } + p.print(formfeed) // terminating comma needs a line break to look good + return + } + + if isIncomplete { + p.print(token.COMMA, newline) + p.print("// "+filteredMsg, newline) + } + + if ws == ignore && mode&noIndent == 0 { + // unindent if we indented + p.print(unindent) + } +} + +type paramMode int + +const ( + funcParam paramMode = iota + funcTParam + typeTParam +) + +func (p *printer) parameters(fields *ast.FieldList, mode paramMode) { + openTok, closeTok := token.LPAREN, token.RPAREN + if mode != funcParam { + openTok, closeTok = token.LBRACK, token.RBRACK + } + p.setPos(fields.Opening) + p.print(openTok) + if len(fields.List) > 0 { + prevLine := p.lineFor(fields.Opening) + ws := indent + for i, par := range fields.List { + // determine par begin and end line (may be different + // if there are multiple parameter names for this par + // or the type is on a separate line) + parLineBeg := p.lineFor(par.Pos()) + parLineEnd := p.lineFor(par.End()) + // separating "," if needed + needsLinebreak := 0 < prevLine && prevLine < parLineBeg + if i > 0 { + // use position of parameter following the comma as + // comma position for correct comma placement, but + // only if the next parameter is on the same line + if !needsLinebreak { + p.setPos(par.Pos()) + } + p.print(token.COMMA) + } + // separator if needed (linebreak or blank) + if needsLinebreak && p.linebreak(parLineBeg, 0, ws, true) > 0 { + // break line if the opening "(" or previous parameter ended on a different line + ws = ignore + } else if i > 0 { + p.print(blank) + } + // parameter names + if len(par.Names) > 0 { + // Very subtle: If we indented before (ws == ignore), identList + // won't indent again. If we didn't (ws == indent), identList will + // indent if the identList spans multiple lines, and it will outdent + // again at the end (and still ws == indent). Thus, a subsequent indent + // by a linebreak call after a type, or in the next multi-line identList + // will do the right thing. + p.identList(par.Names, ws == indent) + p.print(blank) + } + // parameter type + p.expr(stripParensAlways(par.Type)) + prevLine = parLineEnd + } + + // if the closing ")" is on a separate line from the last parameter, + // print an additional "," and line break + if closing := p.lineFor(fields.Closing); 0 < prevLine && prevLine < closing { + p.print(token.COMMA) + p.linebreak(closing, 0, ignore, true) + } else if mode == typeTParam && fields.NumFields() == 1 && combinesWithName(fields.List[0].Type) { + // A type parameter list [P T] where the name P and the type expression T syntactically + // combine to another valid (value) expression requires a trailing comma, as in [P *T,] + // (or an enclosing interface as in [P interface(*T)]), so that the type parameter list + // is not parsed as an array length [P*T]. + p.print(token.COMMA) + } + + // unindent if we indented + if ws == ignore { + p.print(unindent) + } + } + + p.setPos(fields.Closing) + p.print(closeTok) +} + +// combinesWithName reports whether a name followed by the expression x +// syntactically combines to another valid (value) expression. For instance +// using *T for x, "name *T" syntactically appears as the expression x*T. +// On the other hand, using P|Q or *P|~Q for x, "name P|Q" or name *P|~Q" +// cannot be combined into a valid (value) expression. +func combinesWithName(x ast.Expr) bool { + switch x := x.(type) { + case *ast.StarExpr: + // name *x.X combines to name*x.X if x.X is not a type element + return !isTypeElem(x.X) + case *ast.BinaryExpr: + return combinesWithName(x.X) && !isTypeElem(x.Y) + case *ast.ParenExpr: + // name(x) combines but we are making sure at + // the call site that x is never parenthesized. + panic("unexpected parenthesized expression") + } + return false +} + +// isTypeElem reports whether x is a (possibly parenthesized) type element expression. +// The result is false if x could be a type element OR an ordinary (value) expression. +func isTypeElem(x ast.Expr) bool { + switch x := x.(type) { + case *ast.ArrayType, *ast.StructType, *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.ChanType: + return true + case *ast.UnaryExpr: + return x.Op == token.TILDE + case *ast.BinaryExpr: + return isTypeElem(x.X) || isTypeElem(x.Y) + case *ast.ParenExpr: + return isTypeElem(x.X) + } + return false +} + +func (p *printer) signature(sig *ast.FuncType) { + if sig.TypeParams != nil { + p.parameters(sig.TypeParams, funcTParam) + } + if sig.Params != nil { + p.parameters(sig.Params, funcParam) + } else { + p.print(token.LPAREN, token.RPAREN) + } + res := sig.Results + n := res.NumFields() + if n > 0 { + // res != nil + p.print(blank) + if n == 1 && res.List[0].Names == nil { + // single anonymous res; no ()'s + p.expr(stripParensAlways(res.List[0].Type)) + return + } + p.parameters(res, funcParam) + } +} + +func identListSize(list []*ast.Ident, maxSize int) (size int) { + for i, x := range list { + if i > 0 { + size += len(", ") + } + size += utf8.RuneCountInString(x.Name) + if size >= maxSize { + break + } + } + return +} + +func (p *printer) isOneLineFieldList(list []*ast.Field) bool { + if len(list) != 1 { + return false // allow only one field + } + f := list[0] + if f.Tag != nil || f.Comment != nil { + return false // don't allow tags or comments + } + // only name(s) and type + const maxSize = 30 // adjust as appropriate, this is an approximate value + namesSize := identListSize(f.Names, maxSize) + if namesSize > 0 { + namesSize = 1 // blank between names and types + } + typeSize := p.nodeSize(f.Type, maxSize) + return namesSize+typeSize <= maxSize +} + +func (p *printer) setLineComment(text string) { + p.setComment(&ast.CommentGroup{List: []*ast.Comment{{Slash: token.NoPos, Text: text}}}) +} + +func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool) { + lbrace := fields.Opening + list := fields.List + rbrace := fields.Closing + hasComments := isIncomplete || p.commentBefore(p.posFor(rbrace)) + srcIsOneLine := lbrace.IsValid() && rbrace.IsValid() && p.lineFor(lbrace) == p.lineFor(rbrace) + + if !hasComments && srcIsOneLine { + // possibly a one-line struct/interface + if len(list) == 0 { + // no blank between keyword and {} in this case + p.setPos(lbrace) + p.print(token.LBRACE) + p.setPos(rbrace) + p.print(token.RBRACE) + return + } else if p.isOneLineFieldList(list) { + // small enough - print on one line + // (don't use identList and ignore source line breaks) + p.setPos(lbrace) + p.print(token.LBRACE, blank) + f := list[0] + if isStruct { + for i, x := range f.Names { + if i > 0 { + // no comments so no need for comma position + p.print(token.COMMA, blank) + } + p.expr(x) + } + if len(f.Names) > 0 { + p.print(blank) + } + p.expr(f.Type) + } else { // interface + if len(f.Names) > 0 { + name := f.Names[0] // method name + p.expr(name) + p.signature(f.Type.(*ast.FuncType)) // don't print "func" + } else { + // embedded interface + p.expr(f.Type) + } + } + p.print(blank) + p.setPos(rbrace) + p.print(token.RBRACE) + return + } + } + // hasComments || !srcIsOneLine + + p.print(blank) + p.setPos(lbrace) + p.print(token.LBRACE, indent) + if hasComments || len(list) > 0 { + p.print(formfeed) + } + + if isStruct { + + sep := vtab + if len(list) == 1 { + sep = blank + } + var line int + for i, f := range list { + if i > 0 { + p.linebreak(p.lineFor(f.Pos()), 1, ignore, p.linesFrom(line) > 0) + } + extraTabs := 0 + p.setComment(f.Doc) + p.recordLine(&line) + if len(f.Names) > 0 { + // named fields + p.identList(f.Names, false) + p.print(sep) + p.expr(f.Type) + extraTabs = 1 + } else { + // anonymous field + p.expr(f.Type) + extraTabs = 2 + } + if f.Tag != nil { + if len(f.Names) > 0 && sep == vtab { + p.print(sep) + } + p.print(sep) + p.expr(f.Tag) + extraTabs = 0 + } + if f.Comment != nil { + for ; extraTabs > 0; extraTabs-- { + p.print(sep) + } + p.setComment(f.Comment) + } + } + if isIncomplete { + if len(list) > 0 { + p.print(formfeed) + } + p.flush(p.posFor(rbrace), token.RBRACE) // make sure we don't lose the last line comment + p.setLineComment("// " + filteredMsg) + } + + } else { // interface + + var line int + var prev *ast.Ident // previous "type" identifier + for i, f := range list { + var name *ast.Ident // first name, or nil + if len(f.Names) > 0 { + name = f.Names[0] + } + if i > 0 { + // don't do a line break (min == 0) if we are printing a list of types + // TODO(gri) this doesn't work quite right if the list of types is + // spread across multiple lines + min := 1 + if prev != nil && name == prev { + min = 0 + } + p.linebreak(p.lineFor(f.Pos()), min, ignore, p.linesFrom(line) > 0) + } + p.setComment(f.Doc) + p.recordLine(&line) + if name != nil { + // method + p.expr(name) + p.signature(f.Type.(*ast.FuncType)) // don't print "func" + prev = nil + } else { + // embedded interface + p.expr(f.Type) + prev = nil + } + p.setComment(f.Comment) + } + if isIncomplete { + if len(list) > 0 { + p.print(formfeed) + } + p.flush(p.posFor(rbrace), token.RBRACE) // make sure we don't lose the last line comment + p.setLineComment("// contains filtered or unexported methods") + } + + } + p.print(unindent, formfeed) + p.setPos(rbrace) + p.print(token.RBRACE) +} + +// ---------------------------------------------------------------------------- +// Expressions + +func walkBinary(e *ast.BinaryExpr) (has4, has5 bool, maxProblem int) { + switch e.Op.Precedence() { + case 4: + has4 = true + case 5: + has5 = true + } + + switch l := e.X.(type) { + case *ast.BinaryExpr: + if l.Op.Precedence() < e.Op.Precedence() { + // parens will be inserted. + // pretend this is an *ast.ParenExpr and do nothing. + break + } + h4, h5, mp := walkBinary(l) + has4 = has4 || h4 + has5 = has5 || h5 + if maxProblem < mp { + maxProblem = mp + } + } + + switch r := e.Y.(type) { + case *ast.BinaryExpr: + if r.Op.Precedence() <= e.Op.Precedence() { + // parens will be inserted. + // pretend this is an *ast.ParenExpr and do nothing. + break + } + h4, h5, mp := walkBinary(r) + has4 = has4 || h4 + has5 = has5 || h5 + if maxProblem < mp { + maxProblem = mp + } + + case *ast.StarExpr: + if e.Op == token.QUO { // `*/` + maxProblem = 5 + } + + case *ast.UnaryExpr: + switch e.Op.String() + r.Op.String() { + case "/*", "&&", "&^": + maxProblem = 5 + case "++", "--": + if maxProblem < 4 { + maxProblem = 4 + } + } + } + return +} + +func cutoff(e *ast.BinaryExpr, depth int) int { + has4, has5, maxProblem := walkBinary(e) + if maxProblem > 0 { + return maxProblem + 1 + } + if has4 && has5 { + if depth == 1 { + return 5 + } + return 4 + } + if depth == 1 { + return 6 + } + return 4 +} + +func diffPrec(expr ast.Expr, prec int) int { + x, ok := expr.(*ast.BinaryExpr) + if !ok || prec != x.Op.Precedence() { + return 1 + } + return 0 +} + +func reduceDepth(depth int) int { + depth-- + if depth < 1 { + depth = 1 + } + return depth +} + +// Format the binary expression: decide the cutoff and then format. +// Let's call depth == 1 Normal mode, and depth > 1 Compact mode. +// (Algorithm suggestion by Russ Cox.) +// +// The precedences are: +// +// 5 * / % << >> & &^ +// 4 + - | ^ +// 3 == != < <= > >= +// 2 && +// 1 || +// +// The only decision is whether there will be spaces around levels 4 and 5. +// There are never spaces at level 6 (unary), and always spaces at levels 3 and below. +// +// To choose the cutoff, look at the whole expression but excluding primary +// expressions (function calls, parenthesized exprs), and apply these rules: +// +// 1. If there is a binary operator with a right side unary operand +// that would clash without a space, the cutoff must be (in order): +// +// /* 6 +// && 6 +// &^ 6 +// ++ 5 +// -- 5 +// +// (Comparison operators always have spaces around them.) +// +// 2. If there is a mix of level 5 and level 4 operators, then the cutoff +// is 5 (use spaces to distinguish precedence) in Normal mode +// and 4 (never use spaces) in Compact mode. +// +// 3. If there are no level 4 operators or no level 5 operators, then the +// cutoff is 6 (always use spaces) in Normal mode +// and 4 (never use spaces) in Compact mode. +func (p *printer) binaryExpr(x *ast.BinaryExpr, prec1, cutoff, depth int) { + prec := x.Op.Precedence() + if prec < prec1 { + // parenthesis needed + // Note: The parser inserts an ast.ParenExpr node; thus this case + // can only occur if the AST is created in a different way. + p.print(token.LPAREN) + p.expr0(x, reduceDepth(depth)) // parentheses undo one level of depth + p.print(token.RPAREN) + return + } + + printBlank := prec < cutoff + + ws := indent + p.expr1(x.X, prec, depth+diffPrec(x.X, prec)) + if printBlank { + p.print(blank) + } + xline := p.pos.Line // before the operator (it may be on the next line!) + yline := p.lineFor(x.Y.Pos()) + p.setPos(x.OpPos) + p.print(x.Op) + if xline != yline && xline > 0 && yline > 0 { + // at least one line break, but respect an extra empty line + // in the source + if p.linebreak(yline, 1, ws, true) > 0 { + ws = ignore + printBlank = false // no blank after line break + } + } + if printBlank { + p.print(blank) + } + p.expr1(x.Y, prec+1, depth+1) + if ws == ignore { + p.print(unindent) + } +} + +func isBinary(expr ast.Expr) bool { + _, ok := expr.(*ast.BinaryExpr) + return ok +} + +func (p *printer) expr1(expr ast.Expr, prec1, depth int) { + p.setPos(expr.Pos()) + + switch x := expr.(type) { + case *ast.BadExpr: + p.print("BadExpr") + + case *ast.Ident: + p.print(x) + + case *ast.BinaryExpr: + if depth < 1 { + p.internalError("depth < 1:", depth) + depth = 1 + } + p.binaryExpr(x, prec1, cutoff(x, depth), depth) + + case *ast.KeyValueExpr: + p.expr(x.Key) + p.setPos(x.Colon) + p.print(token.COLON, blank) + p.expr(x.Value) + + case *ast.StarExpr: + const prec = token.UnaryPrec + if prec < prec1 { + // parenthesis needed + p.print(token.LPAREN) + p.print(token.MUL) + p.expr(x.X) + p.print(token.RPAREN) + } else { + // no parenthesis needed + p.print(token.MUL) + p.expr(x.X) + } + + case *ast.UnaryExpr: + const prec = token.UnaryPrec + if prec < prec1 { + // parenthesis needed + p.print(token.LPAREN) + p.expr(x) + p.print(token.RPAREN) + } else { + // no parenthesis needed + p.print(x.Op) + if x.Op == token.RANGE { + // TODO(gri) Remove this code if it cannot be reached. + p.print(blank) + } + p.expr1(x.X, prec, depth) + } + + case *ast.BasicLit: + if p.Config.Mode&normalizeNumbers != 0 { + x = normalizedNumber(x) + } + p.print(x) + + case *ast.FuncLit: + p.setPos(x.Type.Pos()) + p.print(token.FUNC) + // See the comment in funcDecl about how the header size is computed. + startCol := p.out.Column - len("func") + p.signature(x.Type) + p.funcBody(p.distanceFrom(x.Type.Pos(), startCol), blank, x.Body) + + case *ast.ParenExpr: + if _, hasParens := x.X.(*ast.ParenExpr); hasParens { + // don't print parentheses around an already parenthesized expression + // TODO(gri) consider making this more general and incorporate precedence levels + p.expr0(x.X, depth) + } else { + p.print(token.LPAREN) + p.expr0(x.X, reduceDepth(depth)) // parentheses undo one level of depth + p.setPos(x.Rparen) + p.print(token.RPAREN) + } + + case *ast.SelectorExpr: + p.selectorExpr(x, depth, false) + + case *ast.TypeAssertExpr: + p.expr1(x.X, token.HighestPrec, depth) + p.print(token.PERIOD) + p.setPos(x.Lparen) + p.print(token.LPAREN) + if x.Type != nil { + p.expr(x.Type) + } else { + p.print(token.TYPE) + } + p.setPos(x.Rparen) + p.print(token.RPAREN) + + case *ast.IndexExpr: + // TODO(gri): should treat[] like parentheses and undo one level of depth + p.expr1(x.X, token.HighestPrec, 1) + p.setPos(x.Lbrack) + p.print(token.LBRACK) + p.expr0(x.Index, depth+1) + p.setPos(x.Rbrack) + p.print(token.RBRACK) + + case *ast.IndexListExpr: + // TODO(gri): as for IndexExpr, should treat [] like parentheses and undo + // one level of depth + p.expr1(x.X, token.HighestPrec, 1) + p.setPos(x.Lbrack) + p.print(token.LBRACK) + p.exprList(x.Lbrack, x.Indices, depth+1, commaTerm, x.Rbrack, false) + p.setPos(x.Rbrack) + p.print(token.RBRACK) + + case *ast.SliceExpr: + // TODO(gri): should treat[] like parentheses and undo one level of depth + p.expr1(x.X, token.HighestPrec, 1) + p.setPos(x.Lbrack) + p.print(token.LBRACK) + indices := []ast.Expr{x.Low, x.High} + if x.Max != nil { + indices = append(indices, x.Max) + } + // determine if we need extra blanks around ':' + var needsBlanks bool + if depth <= 1 { + var indexCount int + var hasBinaries bool + for _, x := range indices { + if x != nil { + indexCount++ + if isBinary(x) { + hasBinaries = true + } + } + } + if indexCount > 1 && hasBinaries { + needsBlanks = true + } + } + for i, x := range indices { + if i > 0 { + if indices[i-1] != nil && needsBlanks { + p.print(blank) + } + p.print(token.COLON) + if x != nil && needsBlanks { + p.print(blank) + } + } + if x != nil { + p.expr0(x, depth+1) + } + } + p.setPos(x.Rbrack) + p.print(token.RBRACK) + + case *ast.CallExpr: + 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 + p.print(token.LPAREN) + wasIndented = p.possibleSelectorExpr(x.Fun, token.HighestPrec, depth) + 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() { + p.exprList(x.Lparen, x.Args, depth, 0, x.Ellipsis, false) + p.setPos(x.Ellipsis) + p.print(token.ELLIPSIS) + if x.Rparen.IsValid() && p.lineFor(x.Ellipsis) < p.lineFor(x.Rparen) { + p.print(token.COMMA, formfeed) + } + } else { + p.exprList(x.Lparen, x.Args, depth, commaTerm, x.Rparen, false) + } + p.setPos(x.Rparen) + p.print(token.RPAREN) + if wasIndented { + p.print(unindent) + } + + case *ast.CompositeLit: + // composite literal elements that are composite literals themselves may have the type omitted + if x.Type != nil { + p.expr1(x.Type, token.HighestPrec, depth) + } + p.level++ + p.setPos(x.Lbrace) + p.print(token.LBRACE) + p.exprList(x.Lbrace, x.Elts, 1, commaTerm, x.Rbrace, x.Incomplete) + // do not insert extra line break following a /*-style comment + // before the closing '}' as it might break the code if there + // is no trailing ',' + mode := noExtraLinebreak + // do not insert extra blank following a /*-style comment + // before the closing '}' unless the literal is empty + if len(x.Elts) > 0 { + mode |= noExtraBlank + } + // need the initial indent to print lone comments with + // the proper level of indentation + p.print(indent, unindent, mode) + p.setPos(x.Rbrace) + p.print(token.RBRACE, mode) + p.level-- + + case *ast.Ellipsis: + p.print(token.ELLIPSIS) + if x.Elt != nil { + p.expr(x.Elt) + } + + case *ast.ArrayType: + p.print(token.LBRACK) + if x.Len != nil { + p.expr(x.Len) + } + p.print(token.RBRACK) + p.expr(x.Elt) + + case *ast.StructType: + p.print(token.STRUCT) + p.fieldList(x.Fields, true, x.Incomplete) + + case *ast.FuncType: + p.print(token.FUNC) + p.signature(x) + + case *ast.InterfaceType: + p.print(token.INTERFACE) + p.fieldList(x.Methods, false, x.Incomplete) + + case *ast.MapType: + p.print(token.MAP, token.LBRACK) + p.expr(x.Key) + p.print(token.RBRACK) + p.expr(x.Value) + + case *ast.ChanType: + switch x.Dir { + case ast.SEND | ast.RECV: + p.print(token.CHAN) + case ast.RECV: + p.print(token.ARROW, token.CHAN) // x.Arrow and x.Pos() are the same + case ast.SEND: + p.print(token.CHAN) + p.setPos(x.Arrow) + p.print(token.ARROW) + } + p.print(blank) + p.expr(x.Value) + + default: + panic("unreachable") + } +} + +// normalizedNumber rewrites base prefixes and exponents +// of numbers to use lower-case letters (0X123 to 0x123 and 1.2E3 to 1.2e3), +// and removes leading 0's from integer imaginary literals (0765i to 765i). +// It leaves hexadecimal digits alone. +// +// normalizedNumber doesn't modify the ast.BasicLit value lit points to. +// If lit is not a number or a number in canonical format already, +// lit is returned as is. Otherwise a new ast.BasicLit is created. +func normalizedNumber(lit *ast.BasicLit) *ast.BasicLit { + if lit.Kind != token.INT && lit.Kind != token.FLOAT && lit.Kind != token.IMAG { + return lit // not a number - nothing to do + } + if len(lit.Value) < 2 { + return lit // only one digit (common case) - nothing to do + } + // len(lit.Value) >= 2 + + // We ignore lit.Kind because for lit.Kind == token.IMAG the literal may be an integer + // or floating-point value, decimal or not. Instead, just consider the literal pattern. + x := lit.Value + switch x[:2] { + default: + // 0-prefix octal, decimal int, or float (possibly with 'i' suffix) + if i := strings.LastIndexByte(x, 'E'); i >= 0 { + x = x[:i] + "e" + x[i+1:] + break + } + // remove leading 0's from integer (but not floating-point) imaginary literals + if x[len(x)-1] == 'i' && !strings.ContainsAny(x, ".e") { + x = strings.TrimLeft(x, "0_") + if x == "i" { + x = "0i" + } + } + case "0X": + x = "0x" + x[2:] + // possibly a hexadecimal float + if i := strings.LastIndexByte(x, 'P'); i >= 0 { + x = x[:i] + "p" + x[i+1:] + } + case "0x": + // possibly a hexadecimal float + i := strings.LastIndexByte(x, 'P') + if i == -1 { + return lit // nothing to do + } + x = x[:i] + "p" + x[i+1:] + case "0O": + x = "0o" + x[2:] + case "0o": + return lit // nothing to do + case "0B": + x = "0b" + x[2:] + case "0b": + return lit // nothing to do + } + + return &ast.BasicLit{ValuePos: lit.ValuePos, Kind: lit.Kind, Value: x} +} + +func (p *printer) possibleSelectorExpr(expr ast.Expr, prec1, depth int) bool { + if x, ok := expr.(*ast.SelectorExpr); ok { + return p.selectorExpr(x, depth, true) + } + p.expr1(expr, prec1, depth) + return false +} + +// selectorExpr handles an *ast.SelectorExpr node and reports whether x spans +// multiple lines. +func (p *printer) selectorExpr(x *ast.SelectorExpr, depth int, isMethod bool) bool { + p.expr1(x.X, token.HighestPrec, depth) + p.print(token.PERIOD) + if line := p.lineFor(x.Sel.Pos()); p.pos.IsValid() && p.pos.Line < line { + p.print(indent, newline) + p.setPos(x.Sel.Pos()) + p.print(x.Sel) + if !isMethod { + p.print(unindent) + } + return true + } + p.setPos(x.Sel.Pos()) + p.print(x.Sel) + return false +} + +func (p *printer) expr0(x ast.Expr, depth int) { + p.expr1(x, token.LowestPrec, depth) +} + +func (p *printer) expr(x ast.Expr) { + const depth = 1 + p.expr1(x, token.LowestPrec, depth) +} + +// ---------------------------------------------------------------------------- +// Statements + +// Print the statement list indented, but without a newline after the last statement. +// Extra line breaks between statements in the source are respected but at most one +// empty line is printed between statements. +func (p *printer) stmtList(list []ast.Stmt, nindent int, nextIsRBrace bool) { + if nindent > 0 { + p.print(indent) + } + var line int + i := 0 + for _, s := range list { + // ignore empty statements (was issue 3466) + if _, isEmpty := s.(*ast.EmptyStmt); !isEmpty { + // nindent == 0 only for lists of switch/select case clauses; + // in those cases each clause is a new section + if len(p.output) > 0 { + // only print line break if we are not at the beginning of the output + // (i.e., we are not printing only a partial program) + p.linebreak(p.lineFor(s.Pos()), 1, ignore, i == 0 || nindent == 0 || p.linesFrom(line) > 0) + } + p.recordLine(&line) + p.stmt(s, nextIsRBrace && i == len(list)-1) + // labeled statements put labels on a separate line, but here + // we only care about the start line of the actual statement + // without label - correct line for each label + for t := s; ; { + lt, _ := t.(*ast.LabeledStmt) + if lt == nil { + break + } + line++ + t = lt.Stmt + } + i++ + } + } + if nindent > 0 { + p.print(unindent) + } +} + +// block prints an *ast.BlockStmt; it always spans at least two lines. +func (p *printer) block(b *ast.BlockStmt, nindent int) { + p.setPos(b.Lbrace) + p.print(token.LBRACE) + p.stmtList(b.List, nindent, true) + p.linebreak(p.lineFor(b.Rbrace), 1, ignore, true) + p.setPos(b.Rbrace) + p.print(token.RBRACE) +} + +func isTypeName(x ast.Expr) bool { + switch t := x.(type) { + case *ast.Ident: + return true + case *ast.SelectorExpr: + return isTypeName(t.X) + } + return false +} + +func stripParens(x ast.Expr) ast.Expr { + if px, strip := x.(*ast.ParenExpr); strip { + // parentheses must not be stripped if there are any + // unparenthesized composite literals starting with + // a type name + ast.Inspect(px.X, func(node ast.Node) bool { + switch x := node.(type) { + case *ast.ParenExpr: + // parentheses protect enclosed composite literals + return false + case *ast.CompositeLit: + if isTypeName(x.Type) { + strip = false // do not strip parentheses + } + return false + } + // in all other cases, keep inspecting + return true + }) + if strip { + return stripParens(px.X) + } + } + return x +} + +func stripParensAlways(x ast.Expr) ast.Expr { + if x, ok := x.(*ast.ParenExpr); ok { + return stripParensAlways(x.X) + } + return x +} + +func (p *printer) controlClause(isForStmt bool, init ast.Stmt, expr ast.Expr, post ast.Stmt) { + p.print(blank) + needsBlank := false + if init == nil && post == nil { + // no semicolons required + if expr != nil { + p.expr(stripParens(expr)) + needsBlank = true + } + } else { + // all semicolons required + // (they are not separators, print them explicitly) + if init != nil { + p.stmt(init, false) + } + p.print(token.SEMICOLON, blank) + if expr != nil { + p.expr(stripParens(expr)) + needsBlank = true + } + if isForStmt { + p.print(token.SEMICOLON, blank) + needsBlank = false + if post != nil { + p.stmt(post, false) + needsBlank = true + } + } + } + if needsBlank { + p.print(blank) + } +} + +// indentList reports whether an expression list would look better if it +// were indented wholesale (starting with the very first element, rather +// than starting at the first line break). +func (p *printer) indentList(list []ast.Expr) bool { + // Heuristic: indentList reports whether there are more than one multi- + // line element in the list, or if there is any element that is not + // starting on the same line as the previous one ends. + if len(list) >= 2 { + b := p.lineFor(list[0].Pos()) + e := p.lineFor(list[len(list)-1].End()) + if 0 < b && b < e { + // list spans multiple lines + n := 0 // multi-line element count + line := b + for _, x := range list { + xb := p.lineFor(x.Pos()) + xe := p.lineFor(x.End()) + if line < xb { + // x is not starting on the same + // line as the previous one ended + return true + } + if xb < xe { + // x is a multi-line element + n++ + } + line = xe + } + return n > 1 + } + } + return false +} + +func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool) { + p.setPos(stmt.Pos()) + + switch s := stmt.(type) { + case *ast.BadStmt: + p.print("BadStmt") + + case *ast.DeclStmt: + p.decl(s.Decl) + + case *ast.EmptyStmt: + // nothing to do + + case *ast.LabeledStmt: + // a "correcting" unindent immediately following a line break + // is applied before the line break if there is no comment + // between (see writeWhitespace) + p.print(unindent) + p.expr(s.Label) + p.setPos(s.Colon) + p.print(token.COLON, indent) + if e, isEmpty := s.Stmt.(*ast.EmptyStmt); isEmpty { + if !nextIsRBrace { + p.print(newline) + p.setPos(e.Pos()) + p.print(token.SEMICOLON) + break + } + } else { + p.linebreak(p.lineFor(s.Stmt.Pos()), 1, ignore, true) + } + p.stmt(s.Stmt, nextIsRBrace) + + case *ast.ExprStmt: + const depth = 1 + p.expr0(s.X, depth) + + case *ast.SendStmt: + const depth = 1 + p.expr0(s.Chan, depth) + p.print(blank) + p.setPos(s.Arrow) + p.print(token.ARROW, blank) + p.expr0(s.Value, depth) + + case *ast.IncDecStmt: + const depth = 1 + p.expr0(s.X, depth+1) + p.setPos(s.TokPos) + p.print(s.Tok) + + case *ast.AssignStmt: + depth := 1 + if len(s.Lhs) > 1 && len(s.Rhs) > 1 { + depth++ + } + p.exprList(s.Pos(), s.Lhs, depth, 0, s.TokPos, false) + p.print(blank) + p.setPos(s.TokPos) + p.print(s.Tok, blank) + p.exprList(s.TokPos, s.Rhs, depth, 0, token.NoPos, false) + + case *ast.GoStmt: + p.print(token.GO, blank) + p.expr(s.Call) + + case *ast.DeferStmt: + p.print(token.DEFER, blank) + p.expr(s.Call) + + case *ast.ReturnStmt: + p.print(token.RETURN) + if s.Results != nil { + p.print(blank) + // Use indentList heuristic to make corner cases look + // better (issue 1207). A more systematic approach would + // always indent, but this would cause significant + // reformatting of the code base and not necessarily + // lead to more nicely formatted code in general. + if p.indentList(s.Results) { + p.print(indent) + // Use NoPos so that a newline never goes before + // the results (see issue #32854). + p.exprList(token.NoPos, s.Results, 1, noIndent, token.NoPos, false) + p.print(unindent) + } else { + p.exprList(token.NoPos, s.Results, 1, 0, token.NoPos, false) + } + } + + case *ast.BranchStmt: + p.print(s.Tok) + if s.Label != nil { + p.print(blank) + p.expr(s.Label) + } + + case *ast.BlockStmt: + p.block(s, 1) + + case *ast.IfStmt: + p.print(token.IF) + p.controlClause(false, s.Init, s.Cond, nil) + p.block(s.Body, 1) + if s.Else != nil { + p.print(blank, token.ELSE, blank) + switch s.Else.(type) { + case *ast.BlockStmt, *ast.IfStmt: + p.stmt(s.Else, nextIsRBrace) + default: + // This can only happen with an incorrectly + // constructed AST. Permit it but print so + // that it can be parsed without errors. + p.print(token.LBRACE, indent, formfeed) + p.stmt(s.Else, true) + p.print(unindent, formfeed, token.RBRACE) + } + } + + case *ast.CaseClause: + if s.List != nil { + p.print(token.CASE, blank) + p.exprList(s.Pos(), s.List, 1, 0, s.Colon, false) + } else { + p.print(token.DEFAULT) + } + p.setPos(s.Colon) + p.print(token.COLON) + p.stmtList(s.Body, 1, nextIsRBrace) + + case *ast.SwitchStmt: + p.print(token.SWITCH) + p.controlClause(false, s.Init, s.Tag, nil) + p.block(s.Body, 0) + + case *ast.TypeSwitchStmt: + p.print(token.SWITCH) + if s.Init != nil { + p.print(blank) + p.stmt(s.Init, false) + p.print(token.SEMICOLON) + } + p.print(blank) + p.stmt(s.Assign, false) + p.print(blank) + p.block(s.Body, 0) + + case *ast.CommClause: + if s.Comm != nil { + p.print(token.CASE, blank) + p.stmt(s.Comm, false) + } else { + p.print(token.DEFAULT) + } + p.setPos(s.Colon) + p.print(token.COLON) + p.stmtList(s.Body, 1, nextIsRBrace) + + case *ast.SelectStmt: + p.print(token.SELECT, blank) + body := s.Body + if len(body.List) == 0 && !p.commentBefore(p.posFor(body.Rbrace)) { + // print empty select statement w/o comments on one line + p.setPos(body.Lbrace) + p.print(token.LBRACE) + p.setPos(body.Rbrace) + p.print(token.RBRACE) + } else { + p.block(body, 0) + } + + case *ast.ForStmt: + p.print(token.FOR) + p.controlClause(true, s.Init, s.Cond, s.Post) + p.block(s.Body, 1) + + case *ast.RangeStmt: + p.print(token.FOR, blank) + if s.Key != nil { + p.expr(s.Key) + if s.Value != nil { + // use position of value following the comma as + // comma position for correct comment placement + p.setPos(s.Value.Pos()) + p.print(token.COMMA, blank) + p.expr(s.Value) + } + p.print(blank) + p.setPos(s.TokPos) + p.print(s.Tok, blank) + } + p.print(token.RANGE, blank) + p.expr(stripParens(s.X)) + p.print(blank) + p.block(s.Body, 1) + + default: + panic("unreachable") + } +} + +// ---------------------------------------------------------------------------- +// Declarations + +// The keepTypeColumn function determines if the type column of a series of +// consecutive const or var declarations must be kept, or if initialization +// values (V) can be placed in the type column (T) instead. The i'th entry +// in the result slice is true if the type column in spec[i] must be kept. +// +// For example, the declaration: +// +// const ( +// foobar int = 42 // comment +// x = 7 // comment +// foo +// bar = 991 +// ) +// +// leads to the type/values matrix below. A run of value columns (V) can +// be moved into the type column if there is no type for any of the values +// in that column (we only move entire columns so that they align properly). +// +// matrix formatted result +// matrix +// T V -> T V -> true there is a T and so the type +// - V - V true column must be kept +// - - - - false +// - V V - false V is moved into T column +func keepTypeColumn(specs []ast.Spec) []bool { + m := make([]bool, len(specs)) + + populate := func(i, j int, keepType bool) { + if keepType { + for ; i < j; i++ { + m[i] = true + } + } + } + + i0 := -1 // if i0 >= 0 we are in a run and i0 is the start of the run + var keepType bool + for i, s := range specs { + t := s.(*ast.ValueSpec) + if t.Values != nil { + if i0 < 0 { + // start of a run of ValueSpecs with non-nil Values + i0 = i + keepType = false + } + } else { + if i0 >= 0 { + // end of a run + populate(i0, i, keepType) + i0 = -1 + } + } + if t.Type != nil { + keepType = true + } + } + if i0 >= 0 { + // end of a run + populate(i0, len(specs), keepType) + } + + return m +} + +func (p *printer) valueSpec(s *ast.ValueSpec, keepType bool) { + p.setComment(s.Doc) + p.identList(s.Names, false) // always present + extraTabs := 3 + if s.Type != nil || keepType { + p.print(vtab) + extraTabs-- + } + if s.Type != nil { + p.expr(s.Type) + } + if s.Values != nil { + p.print(vtab, token.ASSIGN, blank) + p.exprList(token.NoPos, s.Values, 1, 0, token.NoPos, false) + extraTabs-- + } + if s.Comment != nil { + for ; extraTabs > 0; extraTabs-- { + p.print(vtab) + } + p.setComment(s.Comment) + } +} + +func sanitizeImportPath(lit *ast.BasicLit) *ast.BasicLit { + // Note: An unmodified AST generated by go/parser will already + // contain a backward- or double-quoted path string that does + // not contain any invalid characters, and most of the work + // here is not needed. However, a modified or generated AST + // may possibly contain non-canonical paths. Do the work in + // all cases since it's not too hard and not speed-critical. + + // if we don't have a proper string, be conservative and return whatever we have + if lit.Kind != token.STRING { + return lit + } + s, err := strconv.Unquote(lit.Value) + if err != nil { + return lit + } + + // if the string is an invalid path, return whatever we have + // + // spec: "Implementation restriction: A compiler may restrict + // ImportPaths to non-empty strings using only characters belonging + // to Unicode's L, M, N, P, and S general categories (the Graphic + // characters without spaces) and may also exclude the characters + // !"#$%&'()*,:;<=>?[\]^`{|} and the Unicode replacement character + // U+FFFD." + if s == "" { + return lit + } + const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD" + for _, r := range s { + if !unicode.IsGraphic(r) || unicode.IsSpace(r) || strings.ContainsRune(illegalChars, r) { + return lit + } + } + + // otherwise, return the double-quoted path + s = strconv.Quote(s) + if s == lit.Value { + return lit // nothing wrong with lit + } + return &ast.BasicLit{ValuePos: lit.ValuePos, Kind: token.STRING, Value: s} +} + +// The parameter n is the number of specs in the group. If doIndent is set, +// multi-line identifier lists in the spec are indented when the first +// linebreak is encountered. +func (p *printer) spec(spec ast.Spec, n int, doIndent bool) { + switch s := spec.(type) { + case *ast.ImportSpec: + p.setComment(s.Doc) + if s.Name != nil { + p.expr(s.Name) + p.print(blank) + } + p.expr(sanitizeImportPath(s.Path)) + p.setComment(s.Comment) + p.setPos(s.EndPos) + + case *ast.ValueSpec: + if n != 1 { + p.internalError("expected n = 1; got", n) + } + p.setComment(s.Doc) + p.identList(s.Names, doIndent) // always present + if s.Type != nil { + p.print(blank) + p.expr(s.Type) + } + if s.Values != nil { + p.print(blank, token.ASSIGN, blank) + p.exprList(token.NoPos, s.Values, 1, 0, token.NoPos, false) + } + p.setComment(s.Comment) + + case *ast.TypeSpec: + p.setComment(s.Doc) + p.expr(s.Name) + if s.TypeParams != nil { + p.parameters(s.TypeParams, typeTParam) + } + if n == 1 { + p.print(blank) + } else { + p.print(vtab) + } + if s.Assign.IsValid() { + p.print(token.ASSIGN, blank) + } + p.expr(s.Type) + p.setComment(s.Comment) + + default: + panic("unreachable") + } +} + +func (p *printer) genDecl(d *ast.GenDecl) { + p.setComment(d.Doc) + p.setPos(d.Pos()) + p.print(d.Tok, blank) + + if d.Lparen.IsValid() || len(d.Specs) > 1 { + // group of parenthesized declarations + p.setPos(d.Lparen) + p.print(token.LPAREN) + if n := len(d.Specs); n > 0 { + p.print(indent, formfeed) + if n > 1 && (d.Tok == token.CONST || d.Tok == token.VAR) { + // two or more grouped const/var declarations: + // determine if the type column must be kept + keepType := keepTypeColumn(d.Specs) + var line int + for i, s := range d.Specs { + if i > 0 { + p.linebreak(p.lineFor(s.Pos()), 1, ignore, p.linesFrom(line) > 0) + } + p.recordLine(&line) + p.valueSpec(s.(*ast.ValueSpec), keepType[i]) + } + } else { + var line int + for i, s := range d.Specs { + if i > 0 { + p.linebreak(p.lineFor(s.Pos()), 1, ignore, p.linesFrom(line) > 0) + } + p.recordLine(&line) + p.spec(s, n, false) + } + } + p.print(unindent, formfeed) + } + p.setPos(d.Rparen) + p.print(token.RPAREN) + + } else if len(d.Specs) > 0 { + // single declaration + p.spec(d.Specs[0], 1, true) + } +} + +// sizeCounter is an io.Writer which counts the number of bytes written, +// as well as whether a newline character was seen. +type sizeCounter struct { + hasNewline bool + size int +} + +func (c *sizeCounter) Write(p []byte) (int, error) { + if !c.hasNewline { + for _, b := range p { + if b == '\n' || b == '\f' { + c.hasNewline = true + break + } + } + } + c.size += len(p) + return len(p), nil +} + +// nodeSize determines the size of n in chars after formatting. +// The result is <= maxSize if the node fits on one line with at +// most maxSize chars and the formatted output doesn't contain +// any control chars. Otherwise, the result is > maxSize. +func (p *printer) nodeSize(n ast.Node, maxSize int) (size int) { + // nodeSize invokes the printer, which may invoke nodeSize + // recursively. For deep composite literal nests, this can + // lead to an exponential algorithm. Remember previous + // results to prune the recursion (was issue 1628). + if size, found := p.nodeSizes[n]; found { + return size + } + + size = maxSize + 1 // assume n doesn't fit + p.nodeSizes[n] = size + + // nodeSize computation must be independent of particular + // style so that we always get the same decision; print + // in RawFormat + cfg := Config{Mode: RawFormat} + var counter sizeCounter + if err := cfg.fprint(&counter, p.fset, n, p.nodeSizes); err != nil { + return + } + if counter.size <= maxSize && !counter.hasNewline { + // n fits in a single line + size = counter.size + p.nodeSizes[n] = size + } + return +} + +// numLines returns the number of lines spanned by node n in the original source. +func (p *printer) numLines(n ast.Node) int { + if from := n.Pos(); from.IsValid() { + if to := n.End(); to.IsValid() { + return p.lineFor(to) - p.lineFor(from) + 1 + } + } + return infinity +} + +// bodySize is like nodeSize but it is specialized for *ast.BlockStmt's. +func (p *printer) bodySize(b *ast.BlockStmt, maxSize int) int { + pos1 := b.Pos() + pos2 := b.Rbrace + if pos1.IsValid() && pos2.IsValid() && p.lineFor(pos1) != p.lineFor(pos2) { + // opening and closing brace are on different lines - don't make it a one-liner + return maxSize + 1 + } + if len(b.List) > 5 { + // too many statements - don't make it a one-liner + return maxSize + 1 + } + // otherwise, estimate body size + bodySize := p.commentSizeBefore(p.posFor(pos2)) + for i, s := range b.List { + if bodySize > maxSize { + break // no need to continue + } + if i > 0 { + bodySize += 2 // space for a semicolon and blank + } + bodySize += p.nodeSize(s, maxSize) + } + return bodySize +} + +// funcBody prints a function body following a function header of given headerSize. +// If the header's and block's size are "small enough" and the block is "simple enough", +// the block is printed on the current line, without line breaks, spaced from the header +// by sep. Otherwise the block's opening "{" is printed on the current line, followed by +// lines for the block's statements and its closing "}". +func (p *printer) funcBody(headerSize int, sep whiteSpace, b *ast.BlockStmt) { + if b == nil { + return + } + + // save/restore composite literal nesting level + defer func(level int) { + p.level = level + }(p.level) + p.level = 0 + + const maxSize = 100 + if headerSize+p.bodySize(b, maxSize) <= maxSize { + p.print(sep) + p.setPos(b.Lbrace) + p.print(token.LBRACE) + if len(b.List) > 0 { + p.print(blank) + for i, s := range b.List { + if i > 0 { + p.print(token.SEMICOLON, blank) + } + p.stmt(s, i == len(b.List)-1) + } + p.print(blank) + } + p.print(noExtraLinebreak) + p.setPos(b.Rbrace) + p.print(token.RBRACE, noExtraLinebreak) + return + } + + if sep != ignore { + p.print(blank) // always use blank + } + p.block(b, 1) +} + +// distanceFrom returns the column difference between p.out (the current output +// position) and startOutCol. If the start position is on a different line from +// the current position (or either is unknown), the result is infinity. +func (p *printer) distanceFrom(startPos token.Pos, startOutCol int) int { + if startPos.IsValid() && p.pos.IsValid() && p.posFor(startPos).Line == p.pos.Line { + return p.out.Column - startOutCol + } + return infinity +} + +func (p *printer) funcDecl(d *ast.FuncDecl) { + p.setComment(d.Doc) + p.setPos(d.Pos()) + p.print(token.FUNC, blank) + // We have to save startCol only after emitting FUNC; otherwise it can be on a + // different line (all whitespace preceding the FUNC is emitted only when the + // FUNC is emitted). + startCol := p.out.Column - len("func ") + if d.Recv != nil { + p.parameters(d.Recv, funcParam) // method: print receiver + p.print(blank) + } + p.expr(d.Name) + p.signature(d.Type) + p.funcBody(p.distanceFrom(d.Pos(), startCol), vtab, d.Body) +} + +func (p *printer) decl(decl ast.Decl) { + switch d := decl.(type) { + case *ast.BadDecl: + p.setPos(d.Pos()) + p.print("BadDecl") + case *ast.GenDecl: + p.genDecl(d) + case *ast.FuncDecl: + p.funcDecl(d) + default: + panic("unreachable") + } +} + +// ---------------------------------------------------------------------------- +// Files + +func declToken(decl ast.Decl) (tok token.Token) { + tok = token.ILLEGAL + switch d := decl.(type) { + case *ast.GenDecl: + tok = d.Tok + case *ast.FuncDecl: + tok = token.FUNC + } + return +} + +func (p *printer) declList(list []ast.Decl) { + tok := token.ILLEGAL + for _, d := range list { + prev := tok + tok = declToken(d) + // If the declaration token changed (e.g., from CONST to TYPE) + // or the next declaration has documentation associated with it, + // print an empty line between top-level declarations. + // (because p.linebreak is called with the position of d, which + // is past any documentation, the minimum requirement is satisfied + // even w/o the extra getDoc(d) nil-check - leave it in case the + // linebreak logic improves - there's already a TODO). + if len(p.output) > 0 { + // only print line break if we are not at the beginning of the output + // (i.e., we are not printing only a partial program) + min := 1 + if prev != tok || getDoc(d) != nil { + min = 2 + } + // start a new section if the next declaration is a function + // that spans multiple lines (see also issue #19544) + p.linebreak(p.lineFor(d.Pos()), min, ignore, tok == token.FUNC && p.numLines(d) > 1) + } + p.decl(d) + } +} + +func (p *printer) file(src *ast.File) { + p.setComment(src.Doc) + p.setPos(src.Pos()) + p.print(token.PACKAGE, blank) + p.expr(src.Name) + p.declList(src.Decls) + p.print(newline) +} diff --git a/vendor/mvdan.cc/gofumpt/internal/govendor/go/printer/printer.go b/vendor/mvdan.cc/gofumpt/internal/govendor/go/printer/printer.go new file mode 100644 index 00000000..576f9690 --- /dev/null +++ b/vendor/mvdan.cc/gofumpt/internal/govendor/go/printer/printer.go @@ -0,0 +1,1427 @@ +// Copyright 2009 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 printer implements printing of AST nodes. +package printer + +import ( + "fmt" + "go/ast" + "go/build/constraint" + "go/token" + "io" + "os" + "strings" + "sync" + "text/tabwriter" + "unicode" +) + +const ( + maxNewlines = 2 // max. number of newlines between source text + debug = false // enable for debugging + infinity = 1 << 30 +) + +type whiteSpace byte + +const ( + ignore = whiteSpace(0) + blank = whiteSpace(' ') + vtab = whiteSpace('\v') + newline = whiteSpace('\n') + formfeed = whiteSpace('\f') + indent = whiteSpace('>') + unindent = whiteSpace('<') +) + +// A pmode value represents the current printer mode. +type pmode int + +const ( + noExtraBlank pmode = 1 << iota // disables extra blank after /*-style comment + noExtraLinebreak // disables extra line break after /*-style comment +) + +type commentInfo struct { + cindex int // current comment index + comment *ast.CommentGroup // = printer.comments[cindex]; or nil + commentOffset int // = printer.posFor(printer.comments[cindex].List[0].Pos()).Offset; or infinity + commentNewline bool // true if the comment group contains newlines +} + +type printer struct { + // Configuration (does not change after initialization) + Config + fset *token.FileSet + + // Current state + output []byte // raw printer result + indent int // current indentation + level int // level == 0: outside composite literal; level > 0: inside composite literal + mode pmode // current printer mode + endAlignment bool // if set, terminate alignment immediately + impliedSemi bool // if set, a linebreak implies a semicolon + lastTok token.Token // last token printed (token.ILLEGAL if it's whitespace) + prevOpen token.Token // previous non-brace "open" token (, [, or token.ILLEGAL + wsbuf []whiteSpace // delayed white space + goBuild []int // start index of all //go:build comments in output + plusBuild []int // start index of all // +build comments in output + + // Positions + // The out position differs from the pos position when the result + // formatting differs from the source formatting (in the amount of + // white space). If there's a difference and SourcePos is set in + // ConfigMode, //line directives are used in the output to restore + // original source positions for a reader. + pos token.Position // current position in AST (source) space + out token.Position // current position in output space + last token.Position // value of pos after calling writeString + linePtr *int // if set, record out.Line for the next token in *linePtr + + // The list of all source comments, in order of appearance. + comments []*ast.CommentGroup // may be nil + useNodeComments bool // if not set, ignore lead and line comments of nodes + + // Information about p.comments[p.cindex]; set up by nextComment. + commentInfo + + // Cache of already computed node sizes. + nodeSizes map[ast.Node]int + + // Cache of most recently computed line position. + cachedPos token.Pos + cachedLine int // line corresponding to cachedPos +} + +func (p *printer) internalError(msg ...any) { + if debug { + fmt.Print(p.pos.String() + ": ") + fmt.Println(msg...) + panic("mvdan.cc/gofumpt/internal/govendor/go/printer") + } +} + +// commentsHaveNewline reports whether a list of comments belonging to +// an *ast.CommentGroup contains newlines. Because the position information +// may only be partially correct, we also have to read the comment text. +func (p *printer) commentsHaveNewline(list []*ast.Comment) bool { + // len(list) > 0 + line := p.lineFor(list[0].Pos()) + for i, c := range list { + if i > 0 && p.lineFor(list[i].Pos()) != line { + // not all comments on the same line + return true + } + if t := c.Text; len(t) >= 2 && (t[1] == '/' || strings.Contains(t, "\n")) { + return true + } + } + _ = line + return false +} + +func (p *printer) nextComment() { + for p.cindex < len(p.comments) { + c := p.comments[p.cindex] + p.cindex++ + if list := c.List; len(list) > 0 { + p.comment = c + p.commentOffset = p.posFor(list[0].Pos()).Offset + p.commentNewline = p.commentsHaveNewline(list) + return + } + // we should not reach here (correct ASTs don't have empty + // ast.CommentGroup nodes), but be conservative and try again + } + // no more comments + p.commentOffset = infinity +} + +// commentBefore reports whether the current comment group occurs +// before the next position in the source code and printing it does +// not introduce implicit semicolons. +func (p *printer) commentBefore(next token.Position) bool { + return p.commentOffset < next.Offset && (!p.impliedSemi || !p.commentNewline) +} + +// commentSizeBefore returns the estimated size of the +// comments on the same line before the next position. +func (p *printer) commentSizeBefore(next token.Position) int { + // save/restore current p.commentInfo (p.nextComment() modifies it) + defer func(info commentInfo) { + p.commentInfo = info + }(p.commentInfo) + + size := 0 + for p.commentBefore(next) { + for _, c := range p.comment.List { + size += len(c.Text) + } + p.nextComment() + } + return size +} + +// recordLine records the output line number for the next non-whitespace +// token in *linePtr. It is used to compute an accurate line number for a +// formatted construct, independent of pending (not yet emitted) whitespace +// or comments. +func (p *printer) recordLine(linePtr *int) { + p.linePtr = linePtr +} + +// linesFrom returns the number of output lines between the current +// output line and the line argument, ignoring any pending (not yet +// emitted) whitespace or comments. It is used to compute an accurate +// size (in number of lines) for a formatted construct. +func (p *printer) linesFrom(line int) int { + return p.out.Line - line +} + +func (p *printer) posFor(pos token.Pos) token.Position { + // not used frequently enough to cache entire token.Position + return p.fset.PositionFor(pos, false /* absolute position */) +} + +func (p *printer) lineFor(pos token.Pos) int { + if pos != p.cachedPos { + p.cachedPos = pos + p.cachedLine = p.fset.PositionFor(pos, false /* absolute position */).Line + } + return p.cachedLine +} + +// writeLineDirective writes a //line directive if necessary. +func (p *printer) writeLineDirective(pos token.Position) { + if pos.IsValid() && (p.out.Line != pos.Line || p.out.Filename != pos.Filename) { + p.output = append(p.output, tabwriter.Escape) // protect '\n' in //line from tabwriter interpretation + p.output = append(p.output, fmt.Sprintf("//line %s:%d\n", pos.Filename, pos.Line)...) + p.output = append(p.output, tabwriter.Escape) + // p.out must match the //line directive + p.out.Filename = pos.Filename + p.out.Line = pos.Line + } +} + +// writeIndent writes indentation. +func (p *printer) writeIndent() { + // use "hard" htabs - indentation columns + // must not be discarded by the tabwriter + n := p.Config.Indent + p.indent // include base indentation + for i := 0; i < n; i++ { + p.output = append(p.output, '\t') + } + + // update positions + p.pos.Offset += n + p.pos.Column += n + p.out.Column += n +} + +// writeByte writes ch n times to p.output and updates p.pos. +// Only used to write formatting (white space) characters. +func (p *printer) writeByte(ch byte, n int) { + if p.endAlignment { + // Ignore any alignment control character; + // and at the end of the line, break with + // a formfeed to indicate termination of + // existing columns. + switch ch { + case '\t', '\v': + ch = ' ' + case '\n', '\f': + ch = '\f' + p.endAlignment = false + } + } + + if p.out.Column == 1 { + // no need to write line directives before white space + p.writeIndent() + } + + for i := 0; i < n; i++ { + p.output = append(p.output, ch) + } + + // update positions + p.pos.Offset += n + if ch == '\n' || ch == '\f' { + p.pos.Line += n + p.out.Line += n + p.pos.Column = 1 + p.out.Column = 1 + return + } + p.pos.Column += n + p.out.Column += n +} + +// writeString writes the string s to p.output and updates p.pos, p.out, +// and p.last. If isLit is set, s is escaped w/ tabwriter.Escape characters +// to protect s from being interpreted by the tabwriter. +// +// Note: writeString is only used to write Go tokens, literals, and +// comments, all of which must be written literally. Thus, it is correct +// to always set isLit = true. However, setting it explicitly only when +// needed (i.e., when we don't know that s contains no tabs or line breaks) +// avoids processing extra escape characters and reduces run time of the +// printer benchmark by up to 10%. +func (p *printer) writeString(pos token.Position, s string, isLit bool) { + if p.out.Column == 1 { + if p.Config.Mode&SourcePos != 0 { + p.writeLineDirective(pos) + } + p.writeIndent() + } + + if pos.IsValid() { + // update p.pos (if pos is invalid, continue with existing p.pos) + // Note: Must do this after handling line beginnings because + // writeIndent updates p.pos if there's indentation, but p.pos + // is the position of s. + p.pos = pos + } + + if isLit { + // Protect s such that is passes through the tabwriter + // unchanged. Note that valid Go programs cannot contain + // tabwriter.Escape bytes since they do not appear in legal + // UTF-8 sequences. + p.output = append(p.output, tabwriter.Escape) + } + + if debug { + p.output = append(p.output, fmt.Sprintf("/*%s*/", pos)...) // do not update p.pos! + } + p.output = append(p.output, s...) + + // update positions + nlines := 0 + var li int // index of last newline; valid if nlines > 0 + for i := 0; i < len(s); i++ { + // Raw string literals may contain any character except back quote (`). + if ch := s[i]; ch == '\n' || ch == '\f' { + // account for line break + nlines++ + li = i + // A line break inside a literal will break whatever column + // formatting is in place; ignore any further alignment through + // the end of the line. + p.endAlignment = true + } + } + p.pos.Offset += len(s) + if nlines > 0 { + p.pos.Line += nlines + p.out.Line += nlines + c := len(s) - li + p.pos.Column = c + p.out.Column = c + } else { + p.pos.Column += len(s) + p.out.Column += len(s) + } + + if isLit { + p.output = append(p.output, tabwriter.Escape) + } + + p.last = p.pos +} + +// writeCommentPrefix writes the whitespace before a comment. +// If there is any pending whitespace, it consumes as much of +// it as is likely to help position the comment nicely. +// pos is the comment position, next the position of the item +// after all pending comments, prev is the previous comment in +// a group of comments (or nil), and tok is the next token. +func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment, tok token.Token) { + if len(p.output) == 0 { + // the comment is the first item to be printed - don't write any whitespace + return + } + + if pos.IsValid() && pos.Filename != p.last.Filename { + // comment in a different file - separate with newlines + p.writeByte('\f', maxNewlines) + return + } + + if pos.Line == p.last.Line && (prev == nil || prev.Text[1] != '/') { + // comment on the same line as last item: + // separate with at least one separator + hasSep := false + if prev == nil { + // first comment of a comment group + j := 0 + for i, ch := range p.wsbuf { + switch ch { + case blank: + // ignore any blanks before a comment + p.wsbuf[i] = ignore + continue + case vtab: + // respect existing tabs - important + // for proper formatting of commented structs + hasSep = true + continue + case indent: + // apply pending indentation + continue + } + j = i + break + } + p.writeWhitespace(j) + } + // make sure there is at least one separator + if !hasSep { + sep := byte('\t') + if pos.Line == next.Line { + // next item is on the same line as the comment + // (which must be a /*-style comment): separate + // with a blank instead of a tab + sep = ' ' + } + p.writeByte(sep, 1) + } + + } else { + // comment on a different line: + // separate with at least one line break + droppedLinebreak := false + j := 0 + for i, ch := range p.wsbuf { + switch ch { + case blank, vtab: + // ignore any horizontal whitespace before line breaks + p.wsbuf[i] = ignore + continue + case indent: + // apply pending indentation + continue + case unindent: + // if this is not the last unindent, apply it + // as it is (likely) belonging to the last + // construct (e.g., a multi-line expression list) + // and is not part of closing a block + if i+1 < len(p.wsbuf) && p.wsbuf[i+1] == unindent { + continue + } + // if the next token is not a closing }, apply the unindent + // if it appears that the comment is aligned with the + // token; otherwise assume the unindent is part of a + // closing block and stop (this scenario appears with + // comments before a case label where the comments + // apply to the next case instead of the current one) + if tok != token.RBRACE && pos.Column == next.Column { + continue + } + case newline, formfeed: + p.wsbuf[i] = ignore + droppedLinebreak = prev == nil // record only if first comment of a group + } + j = i + break + } + p.writeWhitespace(j) + + // determine number of linebreaks before the comment + n := 0 + if pos.IsValid() && p.last.IsValid() { + n = pos.Line - p.last.Line + if n < 0 { // should never happen + n = 0 + } + } + + // at the package scope level only (p.indent == 0), + // add an extra newline if we dropped one before: + // this preserves a blank line before documentation + // comments at the package scope level (issue 2570) + if p.indent == 0 && droppedLinebreak { + n++ + } + + // make sure there is at least one line break + // if the previous comment was a line comment + if n == 0 && prev != nil && prev.Text[1] == '/' { + n = 1 + } + + if n > 0 { + // use formfeeds to break columns before a comment; + // this is analogous to using formfeeds to separate + // individual lines of /*-style comments + p.writeByte('\f', nlimit(n)) + } + } +} + +// Returns true if s contains only white space +// (only tabs and blanks can appear in the printer's context). +func isBlank(s string) bool { + for i := 0; i < len(s); i++ { + if s[i] > ' ' { + return false + } + } + return true +} + +// commonPrefix returns the common prefix of a and b. +func commonPrefix(a, b string) string { + i := 0 + for i < len(a) && i < len(b) && a[i] == b[i] && (a[i] <= ' ' || a[i] == '*') { + i++ + } + return a[0:i] +} + +// trimRight returns s with trailing whitespace removed. +func trimRight(s string) string { + return strings.TrimRightFunc(s, unicode.IsSpace) +} + +// stripCommonPrefix removes a common prefix from /*-style comment lines (unless no +// comment line is indented, all but the first line have some form of space prefix). +// The prefix is computed using heuristics such that is likely that the comment +// contents are nicely laid out after re-printing each line using the printer's +// current indentation. +func stripCommonPrefix(lines []string) { + if len(lines) <= 1 { + return // at most one line - nothing to do + } + // len(lines) > 1 + + // The heuristic in this function tries to handle a few + // common patterns of /*-style comments: Comments where + // the opening /* and closing */ are aligned and the + // rest of the comment text is aligned and indented with + // blanks or tabs, cases with a vertical "line of stars" + // on the left, and cases where the closing */ is on the + // same line as the last comment text. + + // Compute maximum common white prefix of all but the first, + // last, and blank lines, and replace blank lines with empty + // lines (the first line starts with /* and has no prefix). + // In cases where only the first and last lines are not blank, + // such as two-line comments, or comments where all inner lines + // are blank, consider the last line for the prefix computation + // since otherwise the prefix would be empty. + // + // Note that the first and last line are never empty (they + // contain the opening /* and closing */ respectively) and + // thus they can be ignored by the blank line check. + prefix := "" + prefixSet := false + if len(lines) > 2 { + for i, line := range lines[1 : len(lines)-1] { + if isBlank(line) { + lines[1+i] = "" // range starts with lines[1] + } else { + if !prefixSet { + prefix = line + prefixSet = true + } + prefix = commonPrefix(prefix, line) + } + } + } + // If we don't have a prefix yet, consider the last line. + if !prefixSet { + line := lines[len(lines)-1] + prefix = commonPrefix(line, line) + } + + /* + * Check for vertical "line of stars" and correct prefix accordingly. + */ + lineOfStars := false + if p, _, ok := strings.Cut(prefix, "*"); ok { + // remove trailing blank from prefix so stars remain aligned + prefix = strings.TrimSuffix(p, " ") + lineOfStars = true + } else { + // No line of stars present. + // Determine the white space on the first line after the /* + // and before the beginning of the comment text, assume two + // blanks instead of the /* unless the first character after + // the /* is a tab. If the first comment line is empty but + // for the opening /*, assume up to 3 blanks or a tab. This + // whitespace may be found as suffix in the common prefix. + first := lines[0] + if isBlank(first[2:]) { + // no comment text on the first line: + // reduce prefix by up to 3 blanks or a tab + // if present - this keeps comment text indented + // relative to the /* and */'s if it was indented + // in the first place + i := len(prefix) + for n := 0; n < 3 && i > 0 && prefix[i-1] == ' '; n++ { + i-- + } + if i == len(prefix) && i > 0 && prefix[i-1] == '\t' { + i-- + } + prefix = prefix[0:i] + } else { + // comment text on the first line + suffix := make([]byte, len(first)) + n := 2 // start after opening /* + for n < len(first) && first[n] <= ' ' { + suffix[n] = first[n] + n++ + } + if n > 2 && suffix[2] == '\t' { + // assume the '\t' compensates for the /* + suffix = suffix[2:n] + } else { + // otherwise assume two blanks + suffix[0], suffix[1] = ' ', ' ' + suffix = suffix[0:n] + } + // Shorten the computed common prefix by the length of + // suffix, if it is found as suffix of the prefix. + prefix = strings.TrimSuffix(prefix, string(suffix)) + } + } + + // Handle last line: If it only contains a closing */, align it + // with the opening /*, otherwise align the text with the other + // lines. + last := lines[len(lines)-1] + closing := "*/" + before, _, _ := strings.Cut(last, closing) // closing always present + if isBlank(before) { + // last line only contains closing */ + if lineOfStars { + closing = " */" // add blank to align final star + } + lines[len(lines)-1] = prefix + closing + } else { + // last line contains more comment text - assume + // it is aligned like the other lines and include + // in prefix computation + prefix = commonPrefix(prefix, last) + } + + // Remove the common prefix from all but the first and empty lines. + for i, line := range lines { + if i > 0 && line != "" { + lines[i] = line[len(prefix):] + } + } +} + +func (p *printer) writeComment(comment *ast.Comment) { + text := comment.Text + pos := p.posFor(comment.Pos()) + + const linePrefix = "//line " + if strings.HasPrefix(text, linePrefix) && (!pos.IsValid() || pos.Column == 1) { + // Possibly a //-style line directive. + // Suspend indentation temporarily to keep line directive valid. + defer func(indent int) { p.indent = indent }(p.indent) + p.indent = 0 + } + + // shortcut common case of //-style comments + if text[1] == '/' { + if constraint.IsGoBuild(text) { + p.goBuild = append(p.goBuild, len(p.output)) + } else if constraint.IsPlusBuild(text) { + p.plusBuild = append(p.plusBuild, len(p.output)) + } + p.writeString(pos, trimRight(text), true) + return + } + + // for /*-style comments, print line by line and let the + // write function take care of the proper indentation + lines := strings.Split(text, "\n") + + // The comment started in the first column but is going + // to be indented. For an idempotent result, add indentation + // to all lines such that they look like they were indented + // before - this will make sure the common prefix computation + // is the same independent of how many times formatting is + // applied (was issue 1835). + if pos.IsValid() && pos.Column == 1 && p.indent > 0 { + for i, line := range lines[1:] { + lines[1+i] = " " + line + } + } + + stripCommonPrefix(lines) + + // write comment lines, separated by formfeed, + // without a line break after the last line + for i, line := range lines { + if i > 0 { + p.writeByte('\f', 1) + pos = p.pos + } + if len(line) > 0 { + p.writeString(pos, trimRight(line), true) + } + } +} + +// writeCommentSuffix writes a line break after a comment if indicated +// and processes any leftover indentation information. If a line break +// is needed, the kind of break (newline vs formfeed) depends on the +// pending whitespace. The writeCommentSuffix result indicates if a +// newline was written or if a formfeed was dropped from the whitespace +// buffer. +func (p *printer) writeCommentSuffix(needsLinebreak bool) (wroteNewline, droppedFF bool) { + for i, ch := range p.wsbuf { + switch ch { + case blank, vtab: + // ignore trailing whitespace + p.wsbuf[i] = ignore + case indent, unindent: + // don't lose indentation information + case newline, formfeed: + // if we need a line break, keep exactly one + // but remember if we dropped any formfeeds + if needsLinebreak { + needsLinebreak = false + wroteNewline = true + } else { + if ch == formfeed { + droppedFF = true + } + p.wsbuf[i] = ignore + } + } + } + p.writeWhitespace(len(p.wsbuf)) + + // make sure we have a line break + if needsLinebreak { + p.writeByte('\n', 1) + wroteNewline = true + } + + return +} + +// containsLinebreak reports whether the whitespace buffer contains any line breaks. +func (p *printer) containsLinebreak() bool { + for _, ch := range p.wsbuf { + if ch == newline || ch == formfeed { + return true + } + } + return false +} + +// intersperseComments consumes all comments that appear before the next token +// tok and prints it together with the buffered whitespace (i.e., the whitespace +// that needs to be written before the next token). A heuristic is used to mix +// the comments and whitespace. The intersperseComments result indicates if a +// newline was written or if a formfeed was dropped from the whitespace buffer. +func (p *printer) intersperseComments(next token.Position, tok token.Token) (wroteNewline, droppedFF bool) { + var last *ast.Comment + for p.commentBefore(next) { + list := p.comment.List + changed := false + if p.lastTok != token.IMPORT && // do not rewrite cgo's import "C" comments + p.posFor(p.comment.Pos()).Column == 1 && + p.posFor(p.comment.End()+1) == next { + // Unindented comment abutting next token position: + // a top-level doc comment. + list = formatDocComment(list) + changed = true + + if len(p.comment.List) > 0 && len(list) == 0 { + // The doc comment was removed entirely. + // Keep preceding whitespace. + p.writeCommentPrefix(p.posFor(p.comment.Pos()), next, last, tok) + // Change print state to continue at next. + p.pos = next + p.last = next + // There can't be any more comments. + p.nextComment() + return p.writeCommentSuffix(false) + } + } + for _, c := range list { + p.writeCommentPrefix(p.posFor(c.Pos()), next, last, tok) + p.writeComment(c) + last = c + } + // In case list was rewritten, change print state to where + // the original list would have ended. + if len(p.comment.List) > 0 && changed { + last = p.comment.List[len(p.comment.List)-1] + p.pos = p.posFor(last.End()) + p.last = p.pos + } + p.nextComment() + } + + if last != nil { + // If the last comment is a /*-style comment and the next item + // follows on the same line but is not a comma, and not a "closing" + // token immediately following its corresponding "opening" token, + // add an extra separator unless explicitly disabled. Use a blank + // as separator unless we have pending linebreaks, they are not + // disabled, and we are outside a composite literal, in which case + // we want a linebreak (issue 15137). + // TODO(gri) This has become overly complicated. We should be able + // to track whether we're inside an expression or statement and + // use that information to decide more directly. + needsLinebreak := false + if p.mode&noExtraBlank == 0 && + last.Text[1] == '*' && p.lineFor(last.Pos()) == next.Line && + tok != token.COMMA && + (tok != token.RPAREN || p.prevOpen == token.LPAREN) && + (tok != token.RBRACK || p.prevOpen == token.LBRACK) { + if p.containsLinebreak() && p.mode&noExtraLinebreak == 0 && p.level == 0 { + needsLinebreak = true + } else { + p.writeByte(' ', 1) + } + } + // Ensure that there is a line break after a //-style comment, + // before EOF, and before a closing '}' unless explicitly disabled. + if last.Text[1] == '/' || + tok == token.EOF || + tok == token.RBRACE && p.mode&noExtraLinebreak == 0 { + needsLinebreak = true + } + return p.writeCommentSuffix(needsLinebreak) + } + + // no comment was written - we should never reach here since + // intersperseComments should not be called in that case + p.internalError("intersperseComments called without pending comments") + return +} + +// whiteWhitespace writes the first n whitespace entries. +func (p *printer) writeWhitespace(n int) { + // write entries + for i := 0; i < n; i++ { + switch ch := p.wsbuf[i]; ch { + case ignore: + // ignore! + case indent: + p.indent++ + case unindent: + p.indent-- + if p.indent < 0 { + p.internalError("negative indentation:", p.indent) + p.indent = 0 + } + case newline, formfeed: + // A line break immediately followed by a "correcting" + // unindent is swapped with the unindent - this permits + // proper label positioning. If a comment is between + // the line break and the label, the unindent is not + // part of the comment whitespace prefix and the comment + // will be positioned correctly indented. + if i+1 < n && p.wsbuf[i+1] == unindent { + // Use a formfeed to terminate the current section. + // Otherwise, a long label name on the next line leading + // to a wide column may increase the indentation column + // of lines before the label; effectively leading to wrong + // indentation. + p.wsbuf[i], p.wsbuf[i+1] = unindent, formfeed + i-- // do it again + continue + } + fallthrough + default: + p.writeByte(byte(ch), 1) + } + } + + // shift remaining entries down + l := copy(p.wsbuf, p.wsbuf[n:]) + p.wsbuf = p.wsbuf[:l] +} + +// ---------------------------------------------------------------------------- +// Printing interface + +// nlimit limits n to maxNewlines. +func nlimit(n int) int { + if n > maxNewlines { + n = maxNewlines + } + return n +} + +func mayCombine(prev token.Token, next byte) (b bool) { + switch prev { + case token.INT: + b = next == '.' // 1. + case token.ADD: + b = next == '+' // ++ + case token.SUB: + b = next == '-' // -- + case token.QUO: + b = next == '*' // /* + case token.LSS: + b = next == '-' || next == '<' // <- or << + case token.AND: + b = next == '&' || next == '^' // && or &^ + } + return +} + +func (p *printer) setPos(pos token.Pos) { + if pos.IsValid() { + p.pos = p.posFor(pos) // accurate position of next item + } +} + +// print prints a list of "items" (roughly corresponding to syntactic +// tokens, but also including whitespace and formatting information). +// It is the only print function that should be called directly from +// any of the AST printing functions in nodes.go. +// +// Whitespace is accumulated until a non-whitespace token appears. Any +// comments that need to appear before that token are printed first, +// taking into account the amount and structure of any pending white- +// space for best comment placement. Then, any leftover whitespace is +// printed, followed by the actual token. +func (p *printer) print(args ...any) { + for _, arg := range args { + // information about the current arg + var data string + var isLit bool + var impliedSemi bool // value for p.impliedSemi after this arg + + // record previous opening token, if any + switch p.lastTok { + case token.ILLEGAL: + // ignore (white space) + case token.LPAREN, token.LBRACK: + p.prevOpen = p.lastTok + default: + // other tokens followed any opening token + p.prevOpen = token.ILLEGAL + } + + switch x := arg.(type) { + case pmode: + // toggle printer mode + p.mode ^= x + continue + + case whiteSpace: + if x == ignore { + // don't add ignore's to the buffer; they + // may screw up "correcting" unindents (see + // LabeledStmt) + continue + } + i := len(p.wsbuf) + if i == cap(p.wsbuf) { + // Whitespace sequences are very short so this should + // never happen. Handle gracefully (but possibly with + // bad comment placement) if it does happen. + p.writeWhitespace(i) + i = 0 + } + p.wsbuf = p.wsbuf[0 : i+1] + p.wsbuf[i] = x + if x == newline || x == formfeed { + // newlines affect the current state (p.impliedSemi) + // and not the state after printing arg (impliedSemi) + // because comments can be interspersed before the arg + // in this case + p.impliedSemi = false + } + p.lastTok = token.ILLEGAL + continue + + case *ast.Ident: + data = x.Name + impliedSemi = true + p.lastTok = token.IDENT + + case *ast.BasicLit: + data = x.Value + isLit = true + impliedSemi = true + p.lastTok = x.Kind + + case token.Token: + s := x.String() + if mayCombine(p.lastTok, s[0]) { + // the previous and the current token must be + // separated by a blank otherwise they combine + // into a different incorrect token sequence + // (except for token.INT followed by a '.' this + // should never happen because it is taken care + // of via binary expression formatting) + if len(p.wsbuf) != 0 { + p.internalError("whitespace buffer not empty") + } + p.wsbuf = p.wsbuf[0:1] + p.wsbuf[0] = ' ' + } + data = s + // some keywords followed by a newline imply a semicolon + switch x { + case token.BREAK, token.CONTINUE, token.FALLTHROUGH, token.RETURN, + token.INC, token.DEC, token.RPAREN, token.RBRACK, token.RBRACE: + impliedSemi = true + } + p.lastTok = x + + case string: + // incorrect AST - print error message + data = x + isLit = true + impliedSemi = true + p.lastTok = token.STRING + + default: + fmt.Fprintf(os.Stderr, "print: unsupported argument %v (%T)\n", arg, arg) + panic("mvdan.cc/gofumpt/internal/govendor/go/printer type") + } + // data != "" + + next := p.pos // estimated/accurate position of next item + wroteNewline, droppedFF := p.flush(next, p.lastTok) + + // intersperse extra newlines if present in the source and + // if they don't cause extra semicolons (don't do this in + // flush as it will cause extra newlines at the end of a file) + if !p.impliedSemi { + n := nlimit(next.Line - p.pos.Line) + // don't exceed maxNewlines if we already wrote one + if wroteNewline && n == maxNewlines { + n = maxNewlines - 1 + } + if n > 0 { + ch := byte('\n') + if droppedFF { + ch = '\f' // use formfeed since we dropped one before + } + p.writeByte(ch, n) + impliedSemi = false + } + } + + // the next token starts now - record its line number if requested + if p.linePtr != nil { + *p.linePtr = p.out.Line + p.linePtr = nil + } + + p.writeString(next, data, isLit) + p.impliedSemi = impliedSemi + } +} + +// flush prints any pending comments and whitespace occurring textually +// before the position of the next token tok. The flush result indicates +// if a newline was written or if a formfeed was dropped from the whitespace +// buffer. +func (p *printer) flush(next token.Position, tok token.Token) (wroteNewline, droppedFF bool) { + if p.commentBefore(next) { + // if there are comments before the next item, intersperse them + wroteNewline, droppedFF = p.intersperseComments(next, tok) + } else { + // otherwise, write any leftover whitespace + p.writeWhitespace(len(p.wsbuf)) + } + return +} + +// getDoc returns the ast.CommentGroup associated with n, if any. +func getDoc(n ast.Node) *ast.CommentGroup { + switch n := n.(type) { + case *ast.Field: + return n.Doc + case *ast.ImportSpec: + return n.Doc + case *ast.ValueSpec: + return n.Doc + case *ast.TypeSpec: + return n.Doc + case *ast.GenDecl: + return n.Doc + case *ast.FuncDecl: + return n.Doc + case *ast.File: + return n.Doc + } + return nil +} + +func getLastComment(n ast.Node) *ast.CommentGroup { + switch n := n.(type) { + case *ast.Field: + return n.Comment + case *ast.ImportSpec: + return n.Comment + case *ast.ValueSpec: + return n.Comment + case *ast.TypeSpec: + return n.Comment + case *ast.GenDecl: + if len(n.Specs) > 0 { + return getLastComment(n.Specs[len(n.Specs)-1]) + } + case *ast.File: + if len(n.Comments) > 0 { + return n.Comments[len(n.Comments)-1] + } + } + return nil +} + +func (p *printer) printNode(node any) error { + // unpack *CommentedNode, if any + var comments []*ast.CommentGroup + if cnode, ok := node.(*CommentedNode); ok { + node = cnode.Node + comments = cnode.Comments + } + + if comments != nil { + // commented node - restrict comment list to relevant range + n, ok := node.(ast.Node) + if !ok { + goto unsupported + } + beg := n.Pos() + end := n.End() + // if the node has associated documentation, + // include that commentgroup in the range + // (the comment list is sorted in the order + // of the comment appearance in the source code) + if doc := getDoc(n); doc != nil { + beg = doc.Pos() + } + if com := getLastComment(n); com != nil { + if e := com.End(); e > end { + end = e + } + } + // token.Pos values are global offsets, we can + // compare them directly + i := 0 + for i < len(comments) && comments[i].End() < beg { + i++ + } + j := i + for j < len(comments) && comments[j].Pos() < end { + j++ + } + if i < j { + p.comments = comments[i:j] + } + } else if n, ok := node.(*ast.File); ok { + // use ast.File comments, if any + p.comments = n.Comments + } + + // if there are no comments, use node comments + p.useNodeComments = p.comments == nil + + // get comments ready for use + p.nextComment() + + p.print(pmode(0)) + + // format node + switch n := node.(type) { + case ast.Expr: + p.expr(n) + case ast.Stmt: + // A labeled statement will un-indent to position the label. + // Set p.indent to 1 so we don't get indent "underflow". + if _, ok := n.(*ast.LabeledStmt); ok { + p.indent = 1 + } + p.stmt(n, false) + case ast.Decl: + p.decl(n) + case ast.Spec: + p.spec(n, 1, false) + case []ast.Stmt: + // A labeled statement will un-indent to position the label. + // Set p.indent to 1 so we don't get indent "underflow". + for _, s := range n { + if _, ok := s.(*ast.LabeledStmt); ok { + p.indent = 1 + } + } + p.stmtList(n, 0, false) + case []ast.Decl: + p.declList(n) + case *ast.File: + p.file(n) + default: + goto unsupported + } + + return nil + +unsupported: + return fmt.Errorf("mvdan.cc/gofumpt/internal/govendor/go/printer: unsupported node type %T", node) +} + +// ---------------------------------------------------------------------------- +// Trimmer + +// A trimmer is an io.Writer filter for stripping tabwriter.Escape +// characters, trailing blanks and tabs, and for converting formfeed +// and vtab characters into newlines and htabs (in case no tabwriter +// is used). Text bracketed by tabwriter.Escape characters is passed +// through unchanged. +type trimmer struct { + output io.Writer + state int + space []byte +} + +// trimmer is implemented as a state machine. +// It can be in one of the following states: +const ( + inSpace = iota // inside space + inEscape // inside text bracketed by tabwriter.Escapes + inText // inside text +) + +func (p *trimmer) resetSpace() { + p.state = inSpace + p.space = p.space[0:0] +} + +// Design note: It is tempting to eliminate extra blanks occurring in +// whitespace in this function as it could simplify some +// of the blanks logic in the node printing functions. +// However, this would mess up any formatting done by +// the tabwriter. + +var aNewline = []byte("\n") + +func (p *trimmer) Write(data []byte) (n int, err error) { + // invariants: + // p.state == inSpace: + // p.space is unwritten + // p.state == inEscape, inText: + // data[m:n] is unwritten + m := 0 + var b byte + for n, b = range data { + if b == '\v' { + b = '\t' // convert to htab + } + switch p.state { + case inSpace: + switch b { + case '\t', ' ': + p.space = append(p.space, b) + case '\n', '\f': + p.resetSpace() // discard trailing space + _, err = p.output.Write(aNewline) + case tabwriter.Escape: + _, err = p.output.Write(p.space) + p.state = inEscape + m = n + 1 // +1: skip tabwriter.Escape + default: + _, err = p.output.Write(p.space) + p.state = inText + m = n + } + case inEscape: + if b == tabwriter.Escape { + _, err = p.output.Write(data[m:n]) + p.resetSpace() + } + case inText: + switch b { + case '\t', ' ': + _, err = p.output.Write(data[m:n]) + p.resetSpace() + p.space = append(p.space, b) + case '\n', '\f': + _, err = p.output.Write(data[m:n]) + p.resetSpace() + if err == nil { + _, err = p.output.Write(aNewline) + } + case tabwriter.Escape: + _, err = p.output.Write(data[m:n]) + p.state = inEscape + m = n + 1 // +1: skip tabwriter.Escape + } + default: + panic("unreachable") + } + if err != nil { + return + } + } + n = len(data) + + switch p.state { + case inEscape, inText: + _, err = p.output.Write(data[m:n]) + p.resetSpace() + } + + return +} + +// ---------------------------------------------------------------------------- +// Public interface + +// A Mode value is a set of flags (or 0). They control printing. +type Mode uint + +const ( + RawFormat Mode = 1 << iota // do not use a tabwriter; if set, UseSpaces is ignored + TabIndent // use tabs for indentation independent of UseSpaces + UseSpaces // use spaces instead of tabs for alignment + SourcePos // emit //line directives to preserve original source positions +) + +// The mode below is not included in printer's public API because +// editing code text is deemed out of scope. Because this mode is +// unexported, it's also possible to modify or remove it based on +// the evolving needs of mvdan.cc/gofumpt/internal/govendor/go/format and cmd/gofmt without breaking +// users. See discussion in CL 240683. +const ( + // normalizeNumbers means to canonicalize number + // literal prefixes and exponents while printing. + // + // This value is known in and used by mvdan.cc/gofumpt/internal/govendor/go/format and cmd/gofmt. + // It is currently more convenient and performant for those + // packages to apply number normalization during printing, + // rather than by modifying the AST in advance. + normalizeNumbers Mode = 1 << 30 +) + +// A Config node controls the output of Fprint. +type Config struct { + Mode Mode // default: 0 + Tabwidth int // default: 8 + Indent int // default: 0 (all code is indented at least by this much) +} + +var printerPool = sync.Pool{ + New: func() any { + return &printer{ + // Whitespace sequences are short. + wsbuf: make([]whiteSpace, 0, 16), + // We start the printer with a 16K output buffer, which is currently + // larger than about 80% of Go files in the standard library. + output: make([]byte, 0, 16<<10), + } + }, +} + +func newPrinter(cfg *Config, fset *token.FileSet, nodeSizes map[ast.Node]int) *printer { + p := printerPool.Get().(*printer) + *p = printer{ + Config: *cfg, + fset: fset, + pos: token.Position{Line: 1, Column: 1}, + out: token.Position{Line: 1, Column: 1}, + wsbuf: p.wsbuf[:0], + nodeSizes: nodeSizes, + cachedPos: -1, + output: p.output[:0], + } + return p +} + +func (p *printer) free() { + // Hard limit on buffer size; see https://golang.org/issue/23199. + if cap(p.output) > 64<<10 { + return + } + + printerPool.Put(p) +} + +// fprint implements Fprint and takes a nodesSizes map for setting up the printer state. +func (cfg *Config) fprint(output io.Writer, fset *token.FileSet, node any, nodeSizes map[ast.Node]int) (err error) { + // print node + p := newPrinter(cfg, fset, nodeSizes) + defer p.free() + if err = p.printNode(node); err != nil { + return + } + // print outstanding comments + p.impliedSemi = false // EOF acts like a newline + p.flush(token.Position{Offset: infinity, Line: infinity}, token.EOF) + + // output is buffered in p.output now. + // fix //go:build and // +build comments if needed. + p.fixGoBuildLines() + + // redirect output through a trimmer to eliminate trailing whitespace + // (Input to a tabwriter must be untrimmed since trailing tabs provide + // formatting information. The tabwriter could provide trimming + // functionality but no tabwriter is used when RawFormat is set.) + output = &trimmer{output: output} + + // redirect output through a tabwriter if necessary + if cfg.Mode&RawFormat == 0 { + minwidth := cfg.Tabwidth + + padchar := byte('\t') + if cfg.Mode&UseSpaces != 0 { + padchar = ' ' + } + + twmode := tabwriter.DiscardEmptyColumns + if cfg.Mode&TabIndent != 0 { + minwidth = 0 + twmode |= tabwriter.TabIndent + } + + output = tabwriter.NewWriter(output, minwidth, cfg.Tabwidth, 1, padchar, twmode) + } + + // write printer result via tabwriter/trimmer to output + if _, err = output.Write(p.output); err != nil { + return + } + + // flush tabwriter, if any + if tw, _ := output.(*tabwriter.Writer); tw != nil { + err = tw.Flush() + } + + return +} + +// A CommentedNode bundles an AST node and corresponding comments. +// 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 +} + +// 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. +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. +// 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 { + return (&Config{Tabwidth: 8}).Fprint(output, fset, node) +} diff --git a/vendor/mvdan.cc/gofumpt/internal/version/version.go b/vendor/mvdan.cc/gofumpt/internal/version/version.go index 99293048..785b6b31 100644 --- a/vendor/mvdan.cc/gofumpt/internal/version/version.go +++ b/vendor/mvdan.cc/gofumpt/internal/version/version.go @@ -97,6 +97,9 @@ func goVersion() string { return runtime.Version() } -func String() string { +func String(injected string) string { + if injected != "" { + return fmt.Sprintf("%s (%s)", injected, goVersion()) + } return fmt.Sprintf("%s (%s)", gofumptVersion(), goVersion()) }